문서의 선택한 두 판 사이의 차이를 보여줍니다.
| 양쪽 이전 판이전 판다음 판 | 이전 판 | ||
| react:tictactoe [2018/08/02 04:20] – [이동 표시하기] taekgu | react:tictactoe [2025/04/15 10:05] (현재) – 바깥 편집 127.0.0.1 | ||
|---|---|---|---|
| 줄 626: | 줄 626: | ||
| Game의 render메서드에서 해봅시다. | Game의 render메서드에서 해봅시다. | ||
| <code javascript> | <code javascript> | ||
| + | render() { | ||
| + | const history = this.state.history; | ||
| + | const current = history[history.length - 1]; | ||
| + | const winner = calculateWinner(current.squares); | ||
| + | const moves = history.map((step, | ||
| + | const desc = move?" | ||
| + | return ( | ||
| + | <li> | ||
| + | <button onclick={()=> | ||
| + | </li> | ||
| + | ); | ||
| + | }); | ||
| + | | ||
| + | let status; | ||
| + | if (winner) { | ||
| + | status = ' | ||
| + | } else { | ||
| + | status = 'Next player: ' + (this.state.xIsNext ? ' | ||
| + | } | ||
| + | | ||
| + | return ( | ||
| + | <div className=" | ||
| + | <div className=" | ||
| + | <Board | ||
| + | squares={current.squares} | ||
| + | onClick={(i) => this.handleClick(i)} | ||
| + | /> | ||
| + | </ | ||
| + | <div className=" | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | </ | ||
| + | ); | ||
| + | } | ||
| + | </ | ||
| + | 지금까지의 코드는 [[https:// | ||
| + | 히스토리의 각 단계에서 < | ||
| + | |||
| + | > Warning: Each child in an array or integrator should have a unique " | ||
| + | |||
| + | > 경고: 배열이나 이터레이터에 있는 각 자식은 유니크 " | ||
| + | |||
| + | 이 경고의 의미가 무엇인지 얘기해봅시다. | ||
| + | ==== Keys ==== | ||
| + | 아이템 리스트를 랜더링할때 React는 항상 리스트에 있는 각 아이템에 대한 정보를 저장합니다. 만약 상태를 가진 컴포넌트를 랜더링한다면 컴포넌트가 어떻게 실행되는지와 관계없이 상태는 저장 되어야 하고 React는 네이티브 뷰의 뒤에 참고할 것을 저장한다. | ||
| + | |||
| + | 리스트를 업데이트할 때 React는 무엇을 바꿀지 결정해야 합니다. 리스트에 아이템들을 추가하고, | ||
| + | |||
| + | 이 코드가 아래의 코드로 변경된다고 상상해봅시다. | ||
| + | <code javascript> | ||
| + | < | ||
| + | < | ||
| </ | </ | ||
| + | |||
| + | <code javascript> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | 사람의 눈에는 Alexa와 Ben의 자리가 바뀌고 Claudia가 추가된 것처럼 보인다. 하지만 React는 단순한 컴퓨터프로그램이므로 여러분의 의도를 알지 못합니다. React는 리스트의 각 요소에서 key속성을 지정해달라고 요청합니다. 문자열은 형제로부터 각 컴포넌트들을 구분합니다. 이 경우에 alexa, ben, claudia는 구분할 수 있는 키가 됩니다. 만약 아이템들이 데이터베이스의 객체와 일치시켜야 한다면 데이터베이스 ID를 사용하세요. | ||
| + | <code javascript> | ||
| + | <li key={user.id}> | ||
| + | </ | ||
| + | |||
| + | key는 React에서 제공되는 특별한 속성입니다(ref에서 더 확장된 기능). 엘리먼트가 만들어질때 React는 key속성을 가져오고 반환된 엘리먼트에 직접적으로 key를 저장합니다. key가 props의 한 부분으로 보일지라도 이것은 this.props.key로 참조할 수 없습니다. React는 어떤 하휘 엘리먼트가 수정될지 결정하는 동안 알아서 key를 사용합니다. 컴포넌트가 자신의 키를 알 수 있는 방법은 없습니다. | ||
| + | |||
| + | 리스트가 랜더링될 때 React는 새로운 버전의 각 엘리먼트를 가져오고 이전 리스트에서 매칭되는 키를 가진 것을 찾습니다. key가 세트에 추가될 때 컴포넌트는 만들어집니다. 키가 삭제될 때 컴포넌트는 소멸됩니다. 키들은 React가 각 요소를 구별할 수 있도록하여 다시 랜더링하는 것을 무시하고 상태를 유지할 수 있게 합니다. 만약 컴포넌트의 키를 바꾼다면 완전히 지운 후 새롭게 생성됩니다. | ||
| + | |||
| + | **동적으로 리스트를 빌드할 때마다 적당한 키를 할당할 것을 강력 추천합니다.** 만약 적당한 키를 가지지 못한다면 이를 위해 데이터를 재구성하여야 할지도 모릅니다. | ||
| + | |||
| + | 특정한 키를 구분하지 못한다면 React는 경고를 주고 배열 인덱스를 키로 사용합니다. 이는 올바른 선택이 아닙니다. 만약 리스트에 있는 엘리먼트들을 정렬하거나 리스트에 있는 버튼을 통해 지우거나 추가하면 명시적으로 key={i}를 전달하는 방법을 사용한다면 경고를 표시하지는 않지만 동일한 문제를 발생시키므로 대부분의 경우에는 추천하지 않습니다. | ||
| + | |||
| + | 컴포넌트의 키가 전부 다를 필요는 없지만 관련있는 현제들 사이에서는 유니크해야 합니다. | ||
| + | |||
| + | ==== 시간 여행 실행하기 ==== | ||
| + | 이동 리스트르ㅡ 위해 우리는 각 단계에서 유니크ID를 가졌습니다. Game의 render메서드에서 키는 <li key={move}> | ||
| + | <code javascript> | ||
| + | const moves = history.map((step, | ||
| + | const desc = move?' | ||
| + | return ( | ||
| + | <li key={move}> | ||
| + | <button onclick={()=> | ||
| + | </li> | ||
| + | ); | ||
| + | }); | ||
| + | </ | ||
| + | 지금까지의 코드는 [[https:// | ||
| + | |||
| + | 아직 jumpTo가 정의되지 않았기 때문에 이동 버튼을 클릭하면 에러가 발생합니다. 지금 표시된 단계가 무엇인지 알기 위해 Game상태에 새로운 키를 추가해봅시다. | ||
| + | |||
| + | 먼저 Game의 constructor에 stepNumber: 0를 추가해주세요. | ||
| + | <code javascript> | ||
| + | class Game extends React.Component { | ||
| + | constructor(props) { | ||
| + | super(props); | ||
| + | this.state = { | ||
| + | history: [{ | ||
| + | squares: Array(9).fill(null) | ||
| + | }], | ||
| + | stepNumber: 0, | ||
| + | xIsNext: true, | ||
| + | }; | ||
| + | } | ||
| + | </ | ||
| + | 그 다음 각 상태를 업데이트하기 위해 Game의 jumpTo메서드를 정의해봅시다. 이 메서드에서는 xlsNext를 업데이트하고, | ||
| + | |||
| + | Game클래스에 jumpTo메서드를 추가해주세요. | ||
| + | |||
| + | <code javascript> | ||
| + | handleClick(i) { | ||
| + | // this method has not changed | ||
| + | } | ||
| + | jumpTo(step) { | ||
| + | this.setState({ | ||
| + | stepNumber: step, | ||
| + | xlsNext: (step % 2) === 0, | ||
| + | }) | ||
| + | } | ||
| + | render() { // this method has not changed } | ||
| + | </ | ||
| + | Game handleClick에 상태를 업데이트하기 위해 stepNumber: | ||
| + | |||
| + | <code javascript> | ||
| + | handleClick(i) { | ||
| + | const history = this.state.history; | ||
| + | const current = history[history.length - 1]; | ||
| + | const squares = current.squares.slice(); | ||
| + | if (calculateWinner(squares) || squares[i]) { | ||
| + | return; | ||
| + | } | ||
| + | squares[i] = this.state.xIsNext ? ' | ||
| + | this.setState({ | ||
| + | history: history.concat([{ | ||
| + | squares: squares | ||
| + | }]), | ||
| + | stepNumber: history.length, | ||
| + | xIsNext: !this.state.xIsNext, | ||
| + | }); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 이제 히스토리의 각 단계를 알기 위해 Game의 render를 수정할 수 있습니다. | ||
| + | |||
| + | <code javascript> | ||
| + | render() { | ||
| + | const history = this.state.history; | ||
| + | const current = history[this.state.stepNumber]; | ||
| + | const winner = calculateWinner(current.squares); | ||
| + | // the rest has not changed | ||
| + | </ | ||
| + | |||
| + | 지금까지의 코드는 [[https:// | ||
| + | |||
| + | 이제 이동 버튼을 클릭하면 보드는 즉시 그때 표시된 게임으로 변경됩니다. | ||
| + | |||
| + | ==== 마무리 ==== | ||
| + | 틱택토 게임을 플레이 해보세요. | ||
| + | * 틱택토 게임을 플레이 해보세요. | ||
| + | * 한 명의 플레이어가 게임에서 이길 때 이를 알려줍니다. | ||
| + | * 게임이 진행되는 동안 이동 기록이 저장됩니다. | ||
| + | * 게임 보드의 에전 버전을 표시하기 위해 시간을 되돌릴 수 있습니다. | ||
| + | |||
| + | 잘 동작하네요! React가 어떻게 동작하는지 잘 아셨기를 바랍니다. | ||
| + | |||
| + | 최종 결과물은 [[https:// | ||
| + | |||
| + | **시간이 더 있거나 새로운 스킬들을 연습해보고 싶다면 해볼 수 있는 몇 가지 아이디어가 있습니다. 점점 더 어려운 순으로 배치해두었습니다.** | ||
| + | |||
| + | - 움직임 리스트에서 (col, | ||
| + | - 움직임 리스트의 선택된 아이템을 볼드처리하세요. | ||
| + | - 하드코딩한 것들 대신 사각형을 두 개의 루프를 사용하여 Board를 다시 작성하세요. | ||
| + | - 오름차순 혹은 내림차순 뭐든지 움직임을 정렬하는 버튼을 추가해보세요. | ||
| + | - 누군가 이겼을 때 무엇 때문에 이겼는지 세 개의 사각형을 하이라이트하세요. | ||
| + | |||
| + | 튜토리얼이 진행되는 동안 우리는 엘리먼트, | ||