본문 바로가기
React

React(46) 타입스크립트 - api 객체 받아오기 2 (상세페이지)

by 새발개발JA 2021. 9. 11.
반응형

리액트와 타입스크립트를 이용하여 api 객체를 받아오는 방법에 대한 포스팅 순서이다.

 

1. React(45) 타입스크립트 - api 객체 받아오기 (상세페이지) 1

   - api 로 서버에서 채팅방과 유저들의 정보를 받아와 회원 등급에 따른 채팅 리스트를 구현한다.

 

2. React(46) 타입스크립트 - api 객체 받아오기 (상세페이지) 2

   - 구현한 채팅 리스트를 바탕으로 채팅 정보가 들어있는 상세 페이지를 구현해보았다.


React(46) 타입스크립트 - api 객체 받아오기  2 (상세페이지)

 

 

결과 화면  

1. 통신메소드를 통해 컴포넌트 내에서 채팅방 리스트를 받아온다.  [ 더보기 ] 버튼을 누르자.

 

2. [ 더보기 ] 를 누르면 customer 인 유저 정보와 그에 따른 1:1 채팅방 리스트로 연결된다. (상세페이지)

 

** 실제 코드는 리팩토링하여 구조 자체가 바뀌었으며, 초반에 작업한 코드를 모두가 볼 수 있도록 수정하여 참고용으로 포스팅합니다. 혹시 맞지 않는 부분이 있더라도 부족한 코드 양해부탁드립니다 :)

 

 

App.tsx

동적 라우트 기능을 이용하여 id 값에 따라 /user/:id 값도 달라지게 될거다. UserDetail 컴포넌트로 이동해보자

import { ReactElement } from 'react'
import { UserDetail } from 'pages'
import { UserList } from 'component/UserList'

const App = (): ReactElement => {

  return (
      <div className="app">
        <Route exact path="/user/" component={UserList} /> 
        <Route exact path="/user/:id" component={UserDetail} /> 
      </div>						// url에 ' /:id ' 를 넣으면 동적라우팅이 가능하다
  )
}
export default App

 

pages/index.tsx

여기서 page 들을 전역적으로 관리하는 곳이다. 여길 거쳐서 UserDetail 로 가게 된다.

이렇게 해주는 이유는 ? 생각해보자. 추후에 뭐 하나 바뀌면 일일이 컴포넌트 찾아다니면서 수정하기 귀찮기 때문이다. 

(모든건 귀차니즘에서 왔다고 해도 무방할 것이다.)

export { default as UserDetail } from 'pages/UserDetail';

 

pages/UserDetail.tsx

UserList 에서 [ 더보기 ] 버튼을 클릭해서 들어오는 UserDetail 페이지에서는

useEffect 가 처음 한번만 렌더링되고 getList() 를 통해 id 에 맞는 객체를 데려온다. 

그리고 <UserItemDetail > 안에 prop 로 넣어 데이터를 내려보낸다.

import { ReactElement, useState, useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom';
import { User } from 'model/User';
import UserItemDetail from 'component/user/UserItemDetail';
import Header from 'components/Header';
import Footer from 'components/Footer';
import http from 'util/HttpConfig';

interface ParamID {			// url 뒤에 나오는 param값의 타입을 정해주자
  id: string | undefined		// param으로 id 값을 사용할 것이므로, id 의 타입을 정하자
}

const UserDetail = (): ReactElement => {
  const [userItem, setuserItem] = useState<User>() 	// userItem 은 api 객체를 넣을 변수이다
  const history = useHistory()		// '뒤로가기' 기능을 사용하기 위해 useHistory 를 선언
  const id = useParams<ParamID>() 	// useParams으로 url/:id 값을 알수있다.

  useEffect(() => {
    GetUser() 		// 첫 렌더링시 GetUser()가 자동으로 렌더링해서 객체정보를 불러온다.
  }, [])

  async function GetUser() {
    try { 							// 통신 성공시, 			
      if (!id || id === '') {					// id 값이 없을 때 예외 처리
        alert('잘못된 주소를 입력하셨습니다.')   		  	// 경고창 처리해주고	
        history.push('/user') 					// user 페이지로 돌아가기 
      }
      
      // 리스트로 객체정보 받아오기 이때 id 라는 객체 안에 id라는 키 값이 들어있다
      const list = await http.get(`${api}/user/${id.id}`) 
      
      if (list.status !== 200) { 			// 통신 시 에러 처리
        throw new Error(`Response status is "${list.status}"`);
      }

      if (list.data.resultData) { 			// list 로 데이터가 잘 저장되었으면
        const resList: User[] = JSON.parse( 		// resList에 list객체를 json형식으로 변환하자
          JSON.stringify(list.data.resultData)
        )
        setUserItem(resList)				// resList 를 userList 안에 저 ~ 장
      }
    } catch (e) { 		// 통신 실패시 예외 처리  
      alert('정보를 가져오는데 실패하였습니다.')   	// 안내 경고창과 함께
      console.error(e) 					// 콘솔에 에러 메시지 출력 
      history.push('/user')				// user 페이지로 돌아가기
    }
  }

  return (
    <div className="user">
      <Header>
      						// userItem 객체가 있는 경우에만 && 
      						// props 로 통신 메소드로 받아온 api 객체를 넘겨준다.
      {userItem && 
      	<UserItemDetail user={userItem} key={userItem?.id} /> 
      }
      <Footer>
    </div>
  )
}
export default UserDetail

 

UserItemDetail.tsx

부모인 UserDetail 컴포넌트에서 받아온 prop를 interface 로 선언해줘야 한다. 

useEffect 가 처음 한번만 렌더링되고 getChatList() 를 통해 id 에 맞는 객체를 데려온다. 

특정 회원의 채팅리스트가 렌더링되며, 클릭시 selectChatRoom() 함수를 통해 채팅방으로 연결된다.

import { ReactElement, useState, useEffect, useMemo } from 'react';
import { useHistory } from 'react-router';
import { useSelector, useDispatch } from 'react-redux';
import { rootState } from 'redux';
import { setUser } from 'redux/User';
import { setChat } from 'redux/chat/Chat';
import { User } from 'model/User';
import { Chat, GetSimpleText } from 'model/Chat';
import { GetNick } from 'model/User';
import http from 'util/HttpConfig'; 

interface UserItemDetailProps {
  user: User;
}

interface userInfo {
  chatID: string;
  customerID: string;
  nick: string;
}

const UserItemDetail = (props: UserItemDetailProps): ReactElement => {
  const [chatList, setChatList] = useState<ChatInfo[]>([])
  const uid = useSelector((state: rootState) => state.user.uid);
  const history = useHistory()
  const dispatch = useDispatch()

  useEffect(() => {
   getChatList(props.user); 			// props의 user 객체를 매개변수로 넣어 함수를 실행한다.
  }, []);		      			// 최초렌더링 시에만 실행

  // 가공한 채팅 객체 리스트를 가져오는 메소드
  async function getChatList(user: User) {	// 유저정보를 매개변수로 받는다
    const chats: ChatInfo[] = []		// 채팅리스트 배열을 넣을 변수이다
    if (!user.info || user.info.length === 0) {	// user.info 값이 없다면 
      chats.push({				// chats 객체에 아래 데이터를 추가하자
        chatID: user.chatID,			// 매개변수로 받은 객체의 정보들을 하나씩 넣는다
        customerID: user.uid,
        nick: GetNick(user.uid), 
      })
    } else {						// user.info 값이 있다면 
      for (let i = 0; i < user.info.length; i++) {	// 원소를 하나씩 검사한다
        const info = user.info[i]			// info를 user.info의 원소로 명명하자
        if (user.uid !== uid && info.customerID !== uid) {  // uid 가 참가자들거랑 다르다면
          continue;					 	   // 컨티뉴! 걍 스킵!
        }

        let dup = false;			// 채팅방 중복 생성 여부를 검사할 거다
        for (let j = 0; j < chats.length; j++) { // chats 배열도 함께 검사한다.(2중반복문)
          if (info.chatID === chats[j].chatID) { // info 와 chat 안의 chatID가 둘다 같다면
            dup = true;				// 중복이 true (맞으니)
            break;				// 그냥 빠져나오자
          }
        }

        if (!dup) {				// 만약 중복이 여전히 false (아니라면)
          chats.push({				// chats 배열에 info의 데이터를 push 한다
            chatID: info.chatID,
            customerID: user.uid,
            nick: getNick(user.uid),
          })
        }
      }
    }
    
    setChatList(chats); 	// 그렇게 나온 chats 배열은 set 함수를 거쳐 chatList 가 된다.
    return; 
  }
  
  // 채팅 리스트 클릭하면 해당 채팅방으로 이동하는 함수
  async function selectChat(user: User, cid: string) {
    try {				// 통신성공시  
      const res = await http.get( 	// res 에 chatID 에 해당하는 객체를 받아 저장한다.
        `${api}/chat?chatID=${chatID}`
      );
      
      if (list.status !== 200) { 	// 통신 시 에러 처리
        throw new Error(`Response status is "${list.status}"`);
      }
      
      const chat: Chat = JSON.parse( 	        // chat은 가공된 res 객체가 들어갈 변수다
      	JSON.stringify(res.data.resultData[0])  // res객체를 json형식으로 변환하여 넣어놨다
      )
      
      dispatch(setUser(user)); 	  		// dispatch 로 리덕스에 접근해서 user 값으로 저장
      dispatch(setChat(chat)); 	  		// 마찬가지로 리덕스의 chat 값으로 저장 
      history.push('/user/chat'); 		// 다 마쳤으면 /user/chat 으로 라우팅해준다.
      
    } catch (e) { 				// 통신 실패시 예외 처리  
      alert('정보를 가져오지 못했습니다.')   	// 안내 경고창과 함께
      console.error(e) 				// 콘솔에 에러 메시지 출력 
      history.push('/user')			// user 페이지로 돌아가기
    }
  }

  return (
    <div className="chat-info">
      {chatList.map((chat, index) => ( 			// chatList 배열을 map() 함수로 사용하자
        <div
          className="chat-detail"
          key={chat.chatID}				// 가장 최상단의 태그에 key 값을 넣는다
          onClick={() => 				// onClick 시 해당 채팅방에 입장하는 함수를 실행
            selectChat(props.user, chat.chatID)
          }
        >
	  <div>{chat.chatID}</div>
          <div>{chat.customerID}</div>
          <div>{chat.nick}</div>        
        </div>
      ))}
    </div>
  );
};
export default DealItemDetail;

 

 

 

 

 

 

반응형

댓글