componentDidMount에서 API를 호출하다

712025년 10월 25일4

이론은 완벽했다. 이제 톰은 팀에게 실제 페이스북 API와 연동하여 리액트 컴포넌트를 만들어보라는 과제를 부여했다. 크리스가 자원하여 이 작업을 맡았다. 그의 임무는 특정 게시물(Post)의 댓글 목록을 불러오는 <CommentList> 컴포넌트를 만드는 것이었다.

크리스는 먼저 컴포넌트의 기본 구조를 설계했다.

  • props: 부모로부터 postId를 받는다. 어떤 게시물의 댓글을 가져올지 알아야 하기 때문이다.
  • state: isLoading, comments, error 세 가지 상태를 관리한다.
    • isLoading: 댓글을 불러오는 중인지 여부 (초기값: true)
    • comments: 서버에서 받아온 댓글 배열 (초기값: [] 빈 배열)
    • error: 데이터 로딩 중 에러가 발생했는지 여부 (초기값: null)

그는 설계에 따라 React.createClass를 사용하여 컴포넌트의 뼈대를 만들기 시작했다.

var CommentList = React.createClass({
  getInitialState: function() {
    return {
      isLoading: true,
      comments: [],
      error: null
    };
  },

  // ... (여기에 생명주기 메서드와 render 메서드가 들어갈 예정)
});

다음으로, 그는 render 메서드를 작성했다. 이 메서드는 this.state의 값에 따라 세 가지 다른 UI를 보여줘야 했다.

render: function() {
  if (this.state.isLoading) {
    return React.createElement('div', null, '댓글을 불러오는 중입니다...');
  }

  if (this.state.error) {
    return React.createElement('div', {className: 'error'}, '오류가 발생했습니다.');
  }

  // 로딩도 아니고 에러도 아닐 경우, 실제 댓글 목록을 렌더링한다.
  var commentItems = this.state.comments.map(function(comment) {
    // 실제로는 <CommentItem> 컴포넌트를 사용해야 한다.
    return React.createElement('li', {key: comment.id}, comment.text);
  });
  
  return React.createElement('ul', {className: 'comment-list'}, commentItems);
}

이제 가장 중요한 부분, componentDidMount에서 실제 API를 호출하는 로직을 구현할 차례였다. 그는 페이스북의 내부 API 라이브러리를 사용했다.

componentDidMount: function() {
  // this.props.postId를 이용해 댓글 API 엔드포인트에 요청을 보낸다.
  FacebookAPI.get('/posts/' + this.props.postId + '/comments')
    .then(function(response) {
      // 성공적으로 데이터를 받아왔을 때
      this.setState({
        isLoading: false,
        comments: response.data
      });
    }.bind(this)) // 'this'가 컴포넌트를 가리키도록 바인딩
    .catch(function(err) {
      // 오류가 발생했을 때
      this.setState({
        isLoading: false,
        error: err
      });
    }.bind(this));
}

React.createClass는 메서드 내부의 this를 자동으로 컴포넌트 인스턴스에 바인딩해주지만, API 콜백 함수처럼 다른 컨텍스트에서 실행되는 함수의 this까지는 책임져주지 못했다. 크리스는 .bind(this)를 사용하여 콜백 함수 내부에서도 this.setState를 올바르게 호출할 수 있도록 조치했다.

모든 코드가 완성되었다. 크리스는 테스트 페이지에서 <CommentList postId="12345" />를 렌더링했다.

  1. 화면에는 즉시 ‘댓글을 불러오는 중입니다...’라는 메시지가 나타났다.
  2. 브라우저의 네트워크 탭에서는 페이스북 댓글 API로 향하는 요청이 시작되었다.
  3. 몇백 밀리초 후, API가 성공적으로 응답했다. JSON 형태의 댓글 데이터가 도착했다.
  4. then 콜백 안의 setState가 호출되었다.
  5. 순간, 화면의 ‘로딩 중’ 메시지가 사라지고, 서버에서 받아온 실제 댓글들이 목록 형태로 부드럽게 나타났다.

크리스는 다시 한번 테스트했다. 이번에는 일부러 존재하지 않는 postId를 넣어 API 오류를 유발했다. 그러자 화면에는 정확히 ‘오류가 발생했습니다.’라는 메시지가 표시되었다.

완벽했다.
컴포넌트는 스스로의 생명주기에 맞춰 데이터를 가져오고, 성공과 실패의 모든 경우의 수에 따라 자신의 모습을 정확하게 바꾸었다. 이 모든 과정이 단 하나의 컴포넌트 파일 안에 명확하고 예측 가능한 로직으로 담겨 있었다.

크리스는 비로소 리액트의 진정한 힘을 체감했다. 이것은 단순히 UI를 그리는 것을 넘어, 복잡한 비동기 데이터의 흐름을 우아하게 조율하는 강력한 오케스트라 지휘자와도 같았다.