목록으로

React 이터레이션(iteration)

시리즈
2024. 10. 17. AM 11:19:54

배열을 렌더링하기

푸딩캠프 뉴스레터에서 멋진 컨텐츠를 여럿 다뤘습니다. 어떤 컨텐츠가 있냐고요?
이외에도 많은데, 이 매력있고 훌륭한 컨텐츠를 나열해보겠습니다.

컨텐츠 목록 컴포넌트, ContentList

App.jsx 파일이 있는 곳에 ContentList.jsx 파일을 만듭니다. 껍데기 코드만 작성할게요.
그 다음에 이 컴포넌트를 App.jsx 에서 사용합니다.
기존 코드에 ContentList 컴포넌트를 import문으로 가져온 후 Subscription 컴포넌트 아래에 배치했어요. 닫는 태그 없이 / 기호를 마크업 여는 태그 끝에 넣은 점이 눈에 들어오지요? 물론 <ContentList></ContentList>라고 해도 괜찮지만, Subscription 컴포넌트처럼 자식 Element를 넣는 게 아니라면 대개는 <ContentList />과 같이 줄여씁니다.
파일을 저장해도 화면에서 달라지는 건 없습니다.

ConentList 컴포넌트에 데이터 적용

ContentList.jsx 파일을 열고 맨 위에 다음 코드를 추가하세요.
이 데이터를 사용해 출력합니다.
스물 네 개 모두 출력하려면 할 수 있긴 하지만, 귀찮으니 세 개만 출력하겠습니다.

반복문(iteration) 사용하기

여러분은 이미 알고 계실 거예요. 맞습니다. 우리는 반복문을 사용해 귀찮은 일을 피할 거예요. 여러분에게 익숙한 반복문은 아마도 for문일 겁니다. 그렇죠? for문을 이용해 맨 앞에서부터 세 개를 출력해보죠.
이 코드도 동작합니다.

JSX 문법은 JavaScript 구문을 허용하지 않는다

그런데 JSX를 사용해서 로직과 표현단을 구분했는데, 이번엔 표현단 JSX 코드가 로직이 위치하는 영역을 침범했습니다. 만약 제가 여기서 이번 편을 종료한다면 저는 질타를 받을 거예요. 😂 로직단에 있는 JSX 코드를 표현단으로 옮기겠습니다.
보기엔 그리 어색하지 않지만 이 코드는 오류를 일으킵니다.
이 오류는 JSX문에 JavaScript 구문(statement)을 사용해서 발생한 거예요. 표현식(expression)을 먼저 이해하면 구문을 이해하는 데 도움이 되는데요. 표현식은 어떤 결과값으로 계산 또는 평가되는 코드입니다.
이 코드는 4라는 결과값을 반환합니다. 그래서 할당문을 사용할 수 있죠.
그에 반해 구문은 실행 가능한 최소 단위 코드입니다. 말이 어려운데, 우리가 무슨 무슨 문이라고 하는 것들을 생각하면 됩니다. for문(반복문), if문(조건문). 구문은 표현식과 달리 결과값을 반환하지 않아요. 그 자체로 실행하는 단위이죠. 처음 접한 개념이면 어려울지도 모르겠네요. 모르셔도 괜찮아요. if문, switch문, for문, while문은 JSX 문법에서 사용하지 못한다고 기억해도 충분합니다.
JavaScript에서 for문이나 while문을 사용하지 않고 배열을 순환하는 방법이 있습니다. 바로 배열 객체에 있는 메서드죠. 메서드를 호출해 결과값을 반환하는 표현식이므로 JSX문 안에 사용할 수 있습니다.
코드가 생소할텐데 JavaScript 코드라 생각하고 하나 하나 뜯어보면 바로 이해될 거예요.
결과적으로 처음에 for문을 썼던 것과 동일합니다. for문 대신 배열의 map() 메서드를 이용한 표현식을 JSX문 안에 넣은 것뿐입니다. JavaScript를 잘 이해하면 React를 이해하기 쉬워져요. JavaScript 학습을 강조하는 이유를 잘 아시겠죠?

고유한 key prop이 있어야 한다는 경고

웹 브라우저에서 눈으로 보이는 화면은 잘 동작하는데, 로그를 보면 경고가 찍혀있습니다.
React에서 key prop이 무엇이고 어떤 역할을 하는지는 나중에 자세히 다루기로 하고, 일단 신경 쓰이는 경고를 제거하겠습니다. 제거하는 방법은 경고문에 나와있어요. 목록에 있는 각 자식은 고유한 "key" prop을 가져야 한다고 하네요.
map() 메서드가 반환하는 JSX 노드 중 가장 상위(바깥)에 있는 React element에 key={post.id}와 같이 key prop을 전달하도록 코드를 고쳤습니다. 동작은 달라진 게 없지만 경고문은 더이상 출력되지 않습니다.

페이지네이션 UI 적용

페이지 단위로 잘라 나열하기

글 제목이 스물 네 개가 나열되니 알아보기 불편합니다. 다섯 개씩 한 페이지로 묶어서 나열하는, 페이지네이션 UI를 적용해보겠습니다. 우선 간단하게 다섯 개로 나누는 코드를 넣어볼까요?
사용자가 페이지 번호를 클릭하면 해당 페이지에 속하는 글 목록이 나오게 하는 구현은 간단합니다.
  • 페이지 1 : 시작위치 = 0, 끝위치 = 시작위치 + perPage
  • 페이지 2 : 시작위치 = 5, 끝위치 = 시작위치 + perPage
  • 페이지 3 : 시작위치 = 10, 끝위치 = 시작위치 + perPage
  • 페이지 4 : 시작위치 = 15, 끝위치 = 시작위치 + perPage
패턴이 보이지요? 시작 위치는 페이지 값에서 1을 뺀 후 perPage를 곱합니다. 페이지 값은 page에 담고선, 패턴을 코드로 바꾸겠습니다.
짜잔, 잘 동작합니다!

페이지네이션 UI 구현

페이지네이션은 간단한 수학 문제이지만, 자연스러운 사용자 경험을 주려면 생각보다 신경쓸 요소가 많습니다. 우리는 React 학습에 집중해야 하니 페이지네이션 UI는 단순하고 명료하게 구현할게요. 페이지 개수가 10개면 페이지 버튼이 10개, 페이지 개수가 100개면 페이지 버튼이 100개가 나오는 거죠.
총 페이지 개수를 센 다음에 그 개수만큼 순환하며 페이지 값을 버튼의 라벨로 사용해 출력합니다.
글이 스물 네 개이므로 5 페이지까지 있습니다. 이번엔 페이지 버튼을 누르면 그 페이지로 이동하는 구현을 추가합니다.
setPage() 함수를 만들고, 이 함수를 호출할 때마다 page 변수의 값을 1씩 증가시킵니다. 이 함수는 페이지 번호를 누를 때 호출되어야 하니 버튼의 onClick 속성에 전달합니다. 하는 김에 현재 몇 페이지에 있는지 구분하기 좋게 버튼에 disabled 속성도 제어합니다. 페이지 버튼의 페이지값이 현 페이지값과 같으면 클릭하지 못하게 막는 거죠.
하지만, 페이지 버튼을 눌러도 아무 반응이 없습니다.

페이지네이션 상태 관리

React처럼 구현하고 동작하는 라이브러리를 처음 사용하면 JavaScript 변수와 상태값 변수가 서로 다르다는 점에서 혼란스러워 합니다. 우선 상태를 관리하는 상태값 변수와 상태값 갱신 함수를 만드는 코드부터 보겠습니다.
앞서 Subscription 컴포넌트에서 사용해본 useState() 함수가 또 등장했습니다. 자세한 useState() 함수에 대한 내용은 따로 다루겠지만, 여기에서는 조금만 설명해드리겠습니다.
useState()는 함수 이름에서 유추되듯이 상태를(State) 사용(use)하는 함수입니다. React는 각 컴포넌트의 상태를 주시하다가 상태가 변경되면 그 컴포넌트를 다시 렌더링(rerendering)합니다. 우리는 JavaScript 함수를 React 컴포넌트로 사용하잖아요. 함수식 컴포넌트에서 함수는 곧 컴포넌트이고, 컴포넌트를 다시 렌더링한다는 건 함수를 다시 실행시키는 것입니다. 왜냐하면 함수는 실행하면 인자르 전달받으면서 실행하고 반환문(return)을 만나면서 종료합니다. 중간에 멈췄다 마저 실행하지 않습니다. 항상 실행하면 함수의 맥락이 새로 생기고, 반환문 만나면서 맥락은 사라지지요.
함수 안에 startend 변수가 있습니다. 컴포넌트를 다시 렌더링, 즉 함수를 재실행하면 startend는 매번 초기화됩니다. 그래서는 컴포넌트의 상태, 예를 들어 비활성 상태라고 했을 때 이 상태를 활성화했다가 비활성화를 하지 못합니다. 매번 초기화 될테니까요. 그래서 React의 함수식 컴포넌트에서는 상태를 유지하고, React가 변경을 감지할 수 있는 특별한 변수가 필요합니다. 그런 특별한 변수는 바로 상태값 변수이며, useState() 함수로 이 변수를 정의하는 것입니다.
이 함수를 실행하면 두 개 객체를 배열에 담아 반환하는데, 첫 번째는 상태값 변수이며, 두 번째는 상태값 갱신 함수입니다. useState() 함수를 실행할 때 전달하는 인자는 상태값 변수의 초기값입니다.
버튼을 누르면 setPage() 함수에 페이지 값에 1을 더한 값을 인자로 전달하며 호출합니다. 1을 더한 이유는 index가 0부터 시작해서 1을 더해 보정한 거예요. setPage() 함수를 호출하면, 상태가 변경되고, 이 상태 변화를 React가 감지합니다. React는 ContentList 컴포넌트를 다시 렌더링하고, 다시 렌더링이 되었지만 상태값 변수인 page 의 값은 앞서 인자로 전달받은 값으로 유지됩니다.
그 결과 페이지네이션에 있는 페이지 버튼을 누르면 그 버튼의 페이지값을 유지하고, page 값이 유지된 덕분에 start 변수와 end 변수 값이 페이지를 따라 값이 변합니다. 그 결과 posts를 잘라내는 배열의 slice() 메서드가 의도한 대로 동작하는 것입니다.
토이스토리 2기 모집 중!
푸딩캠프 뉴스레터를 구독하면 학습과 성장, 기술에 관해 요약된 컨텐츠를 매주 편하게 받아보실 수 있습니다.
목차