Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4주차] 조유담 미션 제출합니다. #18

Open
wants to merge 45 commits into
base: main
Choose a base branch
from

Conversation

youdame
Copy link

@youdame youdame commented May 3, 2024

🐻 배포 링크

https://react-messenger-19th-beta.vercel.app/contacts

피그마 링크

https://www.figma.com/file/hrrLcfzraIMMgZMvESS7Kx/ceos_%ED%85%94%EB%A0%88%EA%B7%B8%EB%9E%A8-%EB%A6%AC%EB%94%94%EC%9E%90%EC%9D%B8-(Copy)?type=design&node-id=10%3A1457&mode=dev

👩‍💻 구현 기능

  • 3주차에 답장하기 기능을 구현하겠다고 했으나, 전역 상태관리 없이 구현하기 어려울 거 같다고 판단하여 이번주차로 미루었습니다. 하지만 여전히 미완성이네요..^^ 주말에 틈틈히 해보겠습니다. -> 구현 완료
  • 메세지를 클릭하면 해당 메세지에 대한 답장하기 창이 뜨고, 답장할 수 있습니다!
  • 디자이너분이 의도하신 건 슬라이딩 후 답장하기 창이 뜨는 거지만, 거기까지 하진 못했습니다..ㅎㅎ 이후에 시간이 난다면 해보겠습니다.
  • 현재 화면별로 전반적인 휴대폰 UI 크기가 달라지는데 제대로 픽스할 필요가 있어보입니다. -> 수정 완료
  • 페이지에 필요한 검색 기능도 구현했습니다.

🥳 후기

  • 전역 상태 관리 라이브러리를 처음 사용해봤으며 리덕스 툴킷을 사용했습니다. 이전까지 상태관리 라이브러리를 사용하지 않았던 이유는 유저 관련된 상태 값이 변경될 경우 리액트 쿼리를 이용해 서버에 리패치를 해주는 작업을 했기 때문이었는데요. 굳이 필요성을 느끼지 못했고 프롭을 주고 받는 거에 익숙해졌으며 프롭 드릴링이 심각하다고 느껴졌던 경우가 없었습니다. 하지만 이번에는 다른 페이지임에도 같은 상태를 다뤄야하는 경우가 발생했기에 필요성을 느껴 도입하게 되었습니다. 이후 진행할 세오스 프로젝트에서 리덕스 툴킷을 사용할 예정이기 때문에 파트너 나현님과 함께 리덕스 툴킷을 적용해보기로 했습니다.
  • 학교 과제에 치여 과제를 너무 늦게 시작하는 바람에 전반적으로 디테일에 많이 신경쓰지 못해 아쉽습니다. 스터디 전까지 보완해보도록 하겠습니다!

❓ Key Questions

1. Routing이란?

Routing은 사용자가 웹 애플리케이션의 다양한 부분을 방문할 수 있도록 URL을 특정 컴포넌트 뷰와 매핑하는 과정입니다. React와 Next.js에서의 라우팅은 조금 다른 방식으로 구현됩니다. 각각의 라이브러리 및 프레임워크가 제공하는 라우팅 솔루션에 대해 설명하겠습니다.

  • React에서의 Routing
    React 자체에는 내장된 라우팅 시스템이 없습니다. 대신, 주로 react-router-dom 라이브러리를 사용하여 라우팅을 구현합니다. 이 라이브러리는 선언적인 방법으로 라우트를 설정하고 관리할 수 있게 해줍니다.
  • Next.js에서의 Routing
    Next.js는 파일 시스템을 기반으로 한 라우팅을 내장하고 있습니다. pages 디렉토리 내의 파일 구조를 기반으로 자동으로 라우트를 생성합니다.

2. SPA란?

SPA(Single Page Application)는 웹 애플리케이션 또는 웹사이트를 설계하는 접근 방식 중 하나로, 서버로부터 완전한 새로운 페이지를 불러오는 대신 사용자와의 상호작용에 필요한 데이터만 비동기적으로 불러와서 현재 페이지에 동적으로 렌더링하는 방식입니다. 이러한 접근 방식으로 인해 웹 애플리케이션의 사용자 경험이 데스크탑 애플리케이션과 유사한 매끄러운 경험을 제공할 수 있습니다.

SPA의 특징:

  1. 단일 페이지 로드: SPA는 초기에 페이지를 한 번 로드하고, 그 이후의 모든 데이터 교환은 백엔드와의 API 통신을 통해 이루어집니다. 사용자가 다른 부분을 탐색할 때 페이지를 새로고침하거나 재로딩하지 않아도 동적으로 내용이 업데이트됩니다.
  2. 비동기적 데이터 통신: JavaScript를 사용하여 서버로부터 데이터를 요청하고 받아오는 과정이 비동기적으로 이루어집니다. 이는 AJAX 기술을 활용합니다.
  3. 향상된 사용자 경험: 페이지를 새로고침할 필요 없이 원활하게 콘텐츠가 변경되기 때문에, 사용자 경험이 개선됩니다. 애플리케이션이 빠르고 반응적으로 느껴집니다.
  4. 프론트엔드 중심의 개발: SPA는 프론트엔드에 많은 로직을 두고 처리하는 경향이 있습니다. 이는 프론트엔드 프레임워크 및 라이브러리가 중요한 역할을 하게 되는 것을 의미하며, 대표적으로 React, Angular, Vue.js 등이 있습니다.

SPA의 장단점:

장점:

  • 사용자 경험 향상: 페이지 전환 없이 UI가 업데이트되므로 사용자 경험이 부드럽고 빠릅니다.
  • 서버 부하 감소: 필요한 부분만 데이터를 갱신하므로 전체 페이지를 로드하는 것에 비해 서버 부하가 줄어듭니다.
  • 개발 및 유지보수의 용이성: 모던 JavaScript 프레임워크를 사용하여 컴포넌트 기반의 개발이 가능합니다.

단점:

  • 초기 로딩 지연: 애플리케이션의 전체 자산을 처음에 로드해야 하므로 초기 로딩 시간이 길어질 수 있습니다.
  • SEO 최적화 문제: SPA는 동적으로 컨텐츠를 렌더링하기 때문에 검색 엔진 최적화(SEO)에 어려움이 있을 수 있습니다. (이 문제는 서버 사이드 렌더링(SSR)과 같은 기술로 해결 가능합니다.)
  • 보안 취약성: 크로스 사이트 스크립팅(XSS)과 같은 보안 문제에 더 취약할 수 있습니다.

SPA는 사용자와의 인터랙션이 많은 대화형 애플리케이션에 적합한 구조로, 매끄러운 사용자 경험을 제공하고자 할 때 자주 선택됩니다.

3. 상태관리란?

상태 관리란 애플리케이션의 상태를 중앙에서 효율적으로 관리하는 프로세스를 말합니다. 이는 특히 복잡한 사용자 인터페이스를 가진 대규모 애플리케이션에서 중요한 역할을 합니다. 상태 관리를 통해 애플리케이션의 다양한 부분에서 데이터의 일관성을 유지하고, 데이터 흐름을 명확하게 관리하여 개발 및 유지보수의 복잡성을 줄일 수 있습니다.

상태 관리의 중요성

  1. 일관성: 여러 컴포넌트나 페이지에서 동일한 데이터를 사용할 때, 데이터의 변경이 한 곳에서 이루어지면 그 변경이 애플리케이션 전체에 일관되게 반영됩니다.
  2. 예측 가능성: 데이터 흐름을 명확하게 관리함으로써 애플리케이션의 동작을 예측하기 쉬워집니다.
  3. 디버깅 용이성: 중앙에서 데이터를 관리하면 애플리케이션의 상태를 더 쉽게 추적하고 디버깅할 수 있습니다.

리덕스 툴킷(Redux Toolkit)

리덕스 툴킷은 Redux의 공식, 권장 도구 모음으로, Redux를 더 쉽게 사용할 수 있도록 설계되었습니다. 기존의 Redux가 갖는 복잡성과 보일러플레이트 코드를 줄이면서도, Redux의 핵심 개념인 상태의 예측 가능성과 중앙 집중식 관리는 유지합니다.

리덕스 툴킷의 주요 특징

  1. 구성의 단순화: 리덕스 툴킷은 스토어 설정, 리듀서 작성, 액션 정의 등을 간소화하여 개발자가 더 적은 코드로 같은 작업을 수행할 수 있게 돕습니다.
  2. createSlice(): 이 함수는 액션 타입, 액션 생성자 및 리듀서를 한 번에 생성합니다. 이를 통해 리듀서와 액션을 쉽게 정의하고 관리할 수 있습니다.
  3. configureStore(): 이 함수는 Redux 개발자 도구 확장 기능과 미들웨어를 자동으로 설정해주며, 여러 리듀서를 쉽게 결합하여 스토어를 구성할 수 있습니다.
  4. 불변성 유지: 리덕스 툴킷은 내부적으로 Immer 라이브러리를 사용하여 불변성을 자동으로 관리합니다. 이를 통해 개발자는 상태 업데이트 로직을 더 간단하게 작성할 수 있습니다.

사용 후기

원래 리덕스부터 학습한 후 리덕스 툴킷을 적용해봤기 때문에 그 편리함을 더 느낄 수 있었는데요. 리덕스만 사용했을 때는 setState의 파라미터로 불변성을 유지하기 위해 새로운 객체를 생성하여 전달해야했던 것과 마찬가지로 항상 새로운 객체를 리턴해야하는 번거로움이 있었습니다. 하지만, 리덕스 툴킷을 사용하면 변경되어야하는 부분만 코드로 작성하면 됐기 때문에 편리했습니다.

Copy link

@Programming-Seungwan Programming-Seungwan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redux 전역 상태 관리 라이브러리의 설정도 깔끔하시고 React에서 제공하는 다양한 타입스크립트 타입도 잘 사용하시는 것 같아요!
전체적인 UI도 깔끔하게 잘 구현하신 것 같습니다.
다만, 사용자가 특정 웹 페이지에 링크를 타고 타고 자연스럽게 들어오는 것 이외에도 url path에 입력하여 들어오거나, 아니면 새로고침을 통해 동일한 url path에 대해 요청을 보내는 경우(기존의 상태는 사라질 수 있음)를 고려해보시면 더 멋진 코드가 될 것 같아요.

이번 과제도 너무 고생 많으셨습니다(그리고 뉴진스 민지 이미지도 좋았습니다).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

민지 이미지 👍

<GlobalStyle />
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가장 공통적인 레이아웃을 Layout 컴포턴트로 구성하고 변경되는 내용들은 Outlet 컴포넌트로 구현한 로직 좋아요 :)

<GlobalStyle />
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다만, 현재 작성해주신 애플리케이션은 제가 인위적으로 url 주소를 /로 설정하면 아직 크게 완성되지 않은 레이아웃만 보이는 것 같아요. 오히려 이럴 때에는 Navigate 컴포넌트를 이용하여 사용자를 유담님이 원하는 path로 리디렉션 시켜주면 좋을 것 같습니다.

<Route path="/" element={<Navigate replace to="/other" />} /> 의 방식으로 할 수 있으니 공식 문서를 참고해 보시면 좋겠어요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 store 를 프로젝트 루트 디렉터리의 index.tsx 컴포넌트에서 import하여 Provider 컴포넌트의 store 속성으로 바로 사용하시는데, import 문에서 구체적인 파일명을 언급하지 않으면 index.jsindex.ts(여기에서는 tsx) 를 의미하는 것을 잘 알고 계신 것 같습니다


export default Layout;

const LayoutContainer = styled.div<{ $backgroundColor: string }>`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본적으로 html 요소의 속성을 우리가 지정해주기 위해서는 camelCase로 하면 react가 오류나 경고를 띄우는데, 이를 막아주는 $ 를 단어 앞에 붙여주는 컨벤션을 잘 알고 계신 것 같아요 👍

dispatch(toggleParticipants());
};

if (!partner) return null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 유담님께서 배포하신 버전에서는 /chat/user2 경로로 들어갔다고 가정했을 때, 진입까지는 너무 깔끔하게 잘 되는 것 같습니다.
하지만 여기에서 새로고침을 하면 전역 상태로 관리해주고 있는 partner가 초기화되어 null 값이 되고 이에 따라 chatRoom 컴포넌트는 null 을 리턴하고 있는 것으로 보입니다.
이는 useEffect() 훅을 통해 해결할 수 있을 것 같아요. 컴포넌트의 최상단에 useParams() 훅을 이용해 const {chatId} = useParams() 처럼 구조 분해로 받아주고 useEffect(() => { // dispatch를 활용하여 partner 상태를 컴포넌트가 마운트 될 시에 바꿔주기}. []) 이런 식으로요!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요! 상세 페이지(?)로 이동하는 경우에는 useParams가 유용하더라구요! 제가 알기로는 params는 string이라 형변환을 해줘야하는 번거로움이 있지만요..ㅎㅎ


const dispatch = useDispatch();

const handleChatRoomClick = (partnerId: string) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

특정 사용자를 클릭했을 때, 해당 주소로 라우팅을 해주고, 상태를 변경해주시는 로직을 만들어주셨네요!
이것도 참 좋긴한데, 사용자가 화면 UI를 클릭하여 이동하는 케이스 말고 브라우저의 주소창에 입력을 하여 접근하는 경우도 고려하면 좋을 것 같습니다. 저 같은 경우는 자주 이용하는 웹 페이지는 즐겨찾기 폴더에 넣어두고 바로 이동하거든요(꼭 랜딩 페이지가 아니더라도 세부 페이지를 가는 경우를 의미합니다).

이런 경우에는 유담님께서 고려해주신 상황을 통한 것이 아니기 때문에 상태가 의도한 대로 변경되지 않을 수도 있을 것 같아요.

따라서 /chat/:chatId 경로로 이동하는 것을 렌더링하는 컴포넌트가 처음 마운트 될 때, 즉 useEffect(() => {}, []) 의 첫 인자로 전달되는 콜백함수 내에서 강제로 partner 상태를 전역적으로 변경하도록 강제하면 더 좋을 것 같아요

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 배워갑니다!


interface SearchBarProp {
inputValue: string;
setInputValue: Dispatch<SetStateAction<string>>;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유담님 덕분에 react에서 지원하는 타입스크립트인 Dispatch, SetStateAction 를 잘 배워 갑니다!
이것들이 없다면 set상태() 함수들은 단지 void 를 return 한다는 시그니처를 가질 뿐, 어떤 매개변수 타입들이 들어올지 알 수 없는데 두 타입을 활용하면 꼼꼼하게 강제해줄 수 있겠네요! 😊

@@ -0,0 +1,22 @@
import { configureStore } from '@reduxjs/toolkit';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존의 redux 를 이용하면 createStore() 함수를 이용하여 여러 상태들을 합쳐주고 스스로 결정해야할 것들이 너무 많죠!
이보다는 RTK를 이용하여 슬라이스를 생성하고, 리듀서와 액션 생성 함수들을 만들어준 다음 필요한 컴포넌트에서 주도적으로 import 하여 사용하시는 모습은 flux 패턴을 잘 이해하고 계신 것 같아요👍

Comment on lines +19 to +20
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 커스텀 훅을 만들면 프로젝트 루트 디렉터리에 hooks 라는 디렉터리를 하나 생성하고, 여기에서 만든 다음 필요할 때마다 사용하고 있어요.
이건 그냥 제 취향이라 그냥 그런게 있다... 정도만 인지해주셔도 충분할 것 같네요

Copy link

@Dahn12 Dahn12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유담님 이번 과제도 정말 수고 많으셨습니다! 검색 기능과 같은 신경 써야할 기능들이 많아 보이는데 모두 구현하셨다는게 정말 대단해요..! 다음 과제도 화이팅 해봐요😁

Comment on lines +32 to +37
// shift + enter로 줄 바꿈 기능
const handleKeyDownShiftEnter = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.nativeEvent.isComposing) {
// isComposing 이 true 이면
return; // 조합 중이므로 동작을 막는다.
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이부분도 따로 구현을 해주셨네요! 배워갑니당

dispatch(toggleParticipants());
};

if (!partner) return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요! 상세 페이지(?)로 이동하는 경우에는 useParams가 유용하더라구요! 제가 알기로는 params는 string이라 형변환을 해줘야하는 번거로움이 있지만요..ㅎㅎ


const dispatch = useDispatch();

const handleChatRoomClick = (partnerId: string) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 배워갑니다!

Comment on lines +17 to +19
<Link to="/contacts" className={isActivePath('/contacts') ? 'active' : 'inactive'}>
<ProfileIcon alt="연락처 아이콘" />
<p>연락처</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분을 저는 useLocation을 사용했는데 useLocation과 useNavigate도 한번 확인해보지면 좋을거 같아요!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

채팅 검색 기능까지 구현하셨네요! 대단합니당...👍

@@ -0,0 +1,10 @@
declare module '*.svg' {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 svg를 못쓰고있었는데 이렇게 따로 파일로 빼면 되겠네요...! 배워갑니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants