이 에러 때문에 얼마나 고생했던지 수명이 짧아졌을 정도다.
구글링을 하면 대부분이 .py파일을 .txt파일로 실행한 경우에 에러가 발생하는데,
나와 같은 경우 Django로 만든 웹사이트를 runserver하면 명령프롬프트에서는 해당 에러가 나타나고,
웹브라우저에서는 A server error occurred. Please contact the administrator.라는 문장이 노출되었다.
디버그 화면이 나와야하는데 그렇지 않아서 어떻게 손을 써야할지 몰랐다.
사실 힌트는 이미 수십번 찾았다.

open(encoding="utf-8")

템플릿의 유니코드를 utf-8로 인코딩해주는 짧은 코드를 적재적소에 작성하면 되는 것이다.
그 적재적소를 찾는데 오랜 시간이 걸린 것 같다.
[https://chibychi.blogspot.com/2019/04/django-unicodedecodeerror.html?showComment=1556001026219#c6818858330997373051\](https://chibychi.blogspot.com/2019/04/django-unicodedecodeerror.html?showComment=1556001026219#c6818858330997373051)
바로 이 분 덕분에 정확한 위치를 찾아서 코드를 집어넣었다.

가상환경 폴더에서 Lib\site-packages\django\views\debug.py를 찾아

def get_traceback_html(self):
    """Return HTML version of debug 500 HTTP error page."""
    with Path(CURRENT_DIR, 'templates', 'technical_500.html').open() as fh:
        t = DEBUG_ENGINE.from_string(fh.read())
    c = Context(self.get_traceback_data(), use_l10n=False)
    return t.render(c)

비어있던 open() 메서드에 encoding="utf-8"을 기입했다.

def get_traceback\_html(self): 
    """Return HTML version of debug 500 HTTP error page.""" 
    with Path(CURRENT_DIR, 'templates', 'technical_500.html').open(encoding="utf-8") as fh: 
        t = DEBUG\_ENGINE.from_string(fh.read()) 
    c = Context(self.get_traceback_data(), use_l10n=False) 
    return t.render(c) 

가상환경 활성화를 꼭 잊지 않고 실행해야한다.

블로그 이미지

쵸잇

,

강의를 따라 만든 블로그에서 혼자서 새로운 기능 추가하기


  • 원하는 기능: Django Admin에서 카테고리를 선택하는 것을 폼을 통해 만들고 싶었다




HTML Form 요소들 중에 <select> 요소 사용하기


  • HTML Form 요소들을 확인해볼 수 있다. (https://www.w3schools.com/html/html_form_elements.asp)

  • <select> 요소를 사용하면 drop-down list 형태로 만들 수 있다.

  • selected 속성은 옵션을 우선 선택해놓게 한다. 나의 경우 "Category"를 지정했다.

  • disabled 속성은 해당 옵션이 사용되지 않도록 한다. 따라서 "Category"는 타이틀 역할이다.


1
2
3
4
5
<select name="category">
    <option selected="selected" disabled>Category</option>
    <option value="dv">Development</option>
    <option value="ps">Personal</option>
</select>
cs





  • 완성된 모습이다. 기본값처럼 Category가 보이지만 옵션을 disabled한 상태이므로 실제로 선택은 되지 않는다.





블로그 이미지

쵸잇

,

Component State컴포넌트 안에서 쓸 수 있는 객체이다.

그리고 this.setState()를 활용하여 리액트만의 장점을 확인해보자



import React, { Component } from 'react';
import './App.css';
// Movie.js에서 Movie 클래스를 가져온다
import Movie from './Movie'

// extends를 사용하여 Component를 가져와야 App 이름의 컴포넌트를 만들 수 있다
class App extends Component {

  // state는 컴포넌트 안에서 사용되는 객체
  // 특징은 state가 바뀔 때마다 render()가 실행된다

  state = {
    movies: [
      {
        title: "Matrix",
        poster: "https://cdn.vox-cdn.com/thumbor/veUKpCnSKflt86VfCpTfyUqEfYQ=/0x0:1280x720/1200x800/filters:focal(538x258:742x462)/cdn.vox-cdn.com/uploads/chorus_image/image/52198011/c6f5a81cb0fcc1c0e5ae3cba9cc4f40ae35476cb.0.jpeg",
      },
      {
        title: "Oldboy",
        poster: "https://static01.nyt.com/images/2018/10/18/arts/18terracehouse/18terracehouse-articleLarge.jpg?quality=75&auto=webp&disable=upscale",
      },
      {
        title: "Terrace House",
        poster: "https://cdn.vox-cdn.com/thumbor/GltBWgYa-A_SKHFx2CKHEr93Zvw=/0x0:5760x3840/1200x0/filters:focal(0x0:5760x3840):no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/9898277/_DSC_I1A7700.JPG",
      },
      {
        title: "Suits",
        poster: "https://i.ytimg.com/vi/4qBl7_t0M_U/maxresdefault.jpg",
      },
      {
        title: 'Heart Signal',
        poster: 'https://t1.daumcdn.net/cfile/tistory/2165C83C58A206D523',
      }
    ]
  }



App 컴포넌트 밖에 있던 movies 객체를 컴포넌트 안에 위치한 state에 담았다.


  • state는 컴포넌트 안에서만 쓸 수 있는 객체이다.

  • 특이점은 함수를 통해 state에 담긴 값이 변경될 경우 자동으로 render() 메소드를 재실행시킨다.


// 컴포넌트가 마운트(트리에 삽입되면)되면 작동하는 메소드
  // state 값을 설정하면 render를 재실행한다
  componentDidMount(){
    // ()는 function()과 같은 의미
    setTimeout(() => {
      this.setState({
        movies: [
          // state 객체에서 영화정보가 담긴 movies 값을 불러온다
          ...this.state.movies,

          // 새로 추가할 데이터를 입력한다
          {
            title: 'Heart Signal',
            poster: 'https://t1.daumcdn.net/cfile/tistory/2165C83C58A206D523',
          }
      })
    }, 5000)
  }



마운트 후 componentDidMount() 메소드가 호출된다.


  • setTimeout()this.setState() 원하는 시간(5000=5초)이 지나서 호출되도록 하기 위함이다.
  • () => {}function(){}과 동일한 내용이다.
  • ...this.state.movies를 기존 데이터인 moives를 전부 읽어오는 것이다.
  • 따라서 기존 데이터에 새로운 영화 데이터를 추가 입력하여 state 값에 변화를 주었다.   



  render() {
    return (
<div className="App">
        {/* 리스트에 map 메소드를 사용하여 각 객체의 데이터를 컴포넌트로 전달 */}
        {/* index는 각 객체에 id 값을 넣어주며 key값으로 전달한다 */}
        {this.state.movies.map((movie, index) => {
          return <Movie title={movie.title} poster={movie.poster} key={index}/>
        })}
      </div>
    );
  }
}

export default App;



state의 값이 변화가 되면서 render() 메소드는 재실행된다.


  • state에 추가로 저장된 movies 데이터를 Movie 컴포넌트로 전달하여 기존 영화정보에서 5초 후 더 늘어난 영화 정보가 노출된다.




삼항연산자를 활용하여 리액트만의 극적인 효과 나타내기

동시에 나만의 메소드 만들기



class App extends Component {

  state = {
  }

  componentDidMount(){
    setTimeout(() => {
      this.setState({
        movies: [
          {
            title: "Matrix",
            poster: "https://cdn.vox-cdn.com/thumbor/veUKpCnSKflt86VfCpTfyUqEfYQ=/0x0:1280x720/1200x800/filters:focal(538x258:742x462)/cdn.vox-cdn.com/uploads/chorus_image/image/52198011/c6f5a81cb0fcc1c0e5ae3cba9cc4f40ae35476cb.0.jpeg",
          },
          {
            title: "Oldboy",
            poster: "https://static01.nyt.com/images/2018/10/18/arts/18terracehouse/18terracehouse-articleLarge.jpg?quality=75&auto=webp&disable=upscale",
          },
          {
            title: "Terrace House",
            poster: "https://cdn.vox-cdn.com/thumbor/GltBWgYa-A_SKHFx2CKHEr93Zvw=/0x0:5760x3840/1200x0/filters:focal(0x0:5760x3840):no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/9898277/_DSC_I1A7700.JPG",
          },
          {
            title: "Suits",
            poster: "https://i.ytimg.com/vi/4qBl7_t0M_U/maxresdefault.jpg",
          },
          {
            title: 'Heart Signal',
            poster: 'https://t1.daumcdn.net/cfile/tistory/2165C83C58A206D523',
          }
        ]
      })
    }, 5000)
  }



state에 작성했던 movies 값을 비워두자


  • this.setState()movies 값을 그대로 가져온다.



  // _(언더바)를 사용해 나만의 메소드와 리액트 자체 메소드에 구분을 주었다.
  // state에서 movies 값을 map() 메소드에 적용하여 새로운 배열을 만들어 movies 객체에 담아서 반환
  _renderMovies = () => {
    const movies = this.state.movies.map((movie, index) => {
      return <Movie title={movie.title} poster={movie.poster} key={index}/>
    })
    return movies
  }



render() 메소드 안에 작성했던 map() 메소드를 가져온다


  • _renderMovies는 나만의 메소드이므로 리액트 자체 메소드와 구분 짓기 위해 _(언더바)를 사용한다.
  • map()을 통해 만들어진 배열을 새로이 movies 변수에 담아서 반환한다.



  render() {
    return (
      <div className="App">
        {/* 삼항연산자 사용 */}
        {/* 컴포넌트 state에서 movies 값이 있다면 _renderMovies()를 실행하고 없으면 'Loading' 문자 출력 */}
        {this.state.movies ? this._renderMovies() : 'Loading'}
      </div>
    );
  }
}

export default App;



항연산자를 사용하여 state에서 movies 객체의 존재에 따라 데이터 출력을 달리한다.


  • statemovies 객체가 존재하면 _renderMovies()를 실행하여 영화 정보가 출력되도록 한다.
  • statemovies 객체가 없다면 'Loading' 문자만 웹브라우저에 노출된다.
  • 따라서 state에서 아무런 값이 없는 상태로 마운트된 후 5초가 지나서 componentDidMount()가 실행되어 statemovies 객체가 삽입된다.
  • state에 값에 변화가 생기면서 render()를 재실행한 후 _renderMovies()를 통해 Movie 컴포넌트에 영화 정보를 전달하여 웹브라우저에 노출시킨다.

 

'코딩 연습 > React' 카테고리의 다른 글

PropTypes로 데이터 형태 확인하기  (0) 2019.01.07
Array.prototype.map() 활용하기  (0) 2019.01.07
블로그 이미지

쵸잇

,