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주차 기본/심화/공유 과제] API 통신 #7

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

Conversation

daahyunk
Copy link
Member

@daahyunk daahyunk commented Nov 12, 2024

✨ 구현 기능 명세

💡 기본 과제

  • React + TypeScript
  • Axios 라이브러리 사용
  • ThemeProvider, GlobalStyle 사용
  1. 로그인
  • 로그인 타이틀
  • 아이디(이름) 입력 Input
  • 비밀번호 입력 Input
  • 로그인 버튼 (hover시 배경색 바꾸기 (transition 적용))
  • 회원가입 버튼 (회원가입 페이지로 이동)
  1. 회원가입
  • 이름 - 비밀번호 - 취미 입력이 한 페이지에서 일어남 (컴포넌트만 갈아끼우기)
  • 상단에 회원가입 타이틀
  • 하단에는 로그인 페이지로 가는 링크
  1. 회원가입(이름)
  • 이름 입력 Input
  • 다음 버튼 (비밀번호 입력 폼 나옴)
  • Input 비어있을 때 버튼 비활성화
  1. 회원가입(비밀번호)
  • 비밀번호 입력 Input
  • 비밀번호 확인 Input
  • 둘 중 하나라도 비어있으면 버튼 비활성화
  • 두 비밀번호가 다르면 버튼 비활성화
  • 다음 버튼 (취미 입력 폼 나옴)
  1. 회원가입(취미)
  • 취미 입력 Input
  • 회원가입 버튼
  • Input 비어있을 때 버튼 비활성화
  • 회원가입 실패 시 에러메시지 alert 출력
  • 회원가입 성공 시 회원번호 alert 출력하고, login 페이지로 이동
  1. 마이페이지
  • 헤더에 취미, 내 정보 메뉴 탭
  • 헤더에 로그아웃 버튼
  • 로그아웃 버튼 클릭 시 token 저장 정보 삭제하고 로그인 페이지로 이동 (token 저장 위치는 자율)
  • 헤더 취미, 내 정보 취미 페이지, 내 정보 페이지 출력 (1개의 페이지로 구현해도 되고, url 달라도 됨)
  1. 마이페이지(취미)
  • 나의 취미 출력
  • 사용자 번호 입력 Input
  • 검색 버튼
  • 검색 오류시 alert
  • 검색된 취미 출력
  1. 마이페이지(내 정보)
  • 비밀번호만 입력하면 비밀번호만 변경
  • 취미만 입력하면 취미만 변경
  • 둘 다 입력하면 둘다 변경
  • 둘 다 비어있으면 alert

🔥 심화 과제

  • any 사용하지 않기
  1. 회원가입 (이름)
  • 8글자 넘어가도 버튼 비활성화 처리
  • 8글자 넘어가는 것에 대해 에러메시지 출력
  1. 회원가입 (비밀번호)
  • 비밀번호 보이기 버튼 추가
  • 8글자 넘어가도 버튼 비활성화 처리
  • 8글자 넘어가는 것에 대해 에러메시지 출력
  • 비밀번호 불일치 에러 메시지 출력
  • (선택) 에러메시지 한개만 출력해도 됨 (우선순위는 알아서)
  1. 회원가입 (취미)
  • 8글자 넘어가도 버튼 비활성화 처리
  • 8글자 넘어가는 것에 대해 에러메시지 출력

공유과제

제목: 한 번 설정해두면 개발이 편해지는,,, Axios Interceptor

링크 첨부 : https://wave-web.tistory.com/131


❗️ 내가 새로 알게 된 점

1. Promise<>

비동기 작업에서 Promise<>를 사용해서 반환 타입을 지정하는 방법을 알게 되었다.

🎈 Promise<>가 뭐지?
Promise<>는 비동기 작업이 완료되었을 때 결과를 반환할 것을 약속하는 객체이다. 작업이 성공하면 resolve를 통해 원하는 데이터를 반환하고 실패하면 reject로 에러를 전달한다. Promise<> 안에 있는 꺽쇠 괄호 < >는 그 비동기 작업이 성공했을 때 반환될 데이터의 타입을 명시하는 부분이다.

async function fetchData(): Promise<number> {
  // 이 함수는 숫자를 반환하는 Promise
  return 42;
}

위 코드처럼 Promise라고 쓰면 fetchData라는 비동기 함수가 숫자 타입 데이터를 반환할 것을 보장하게 된다. await나 .then()으로 받아올 데이터가 미리 예상 가능한 타입으로 지정되니까 타입스크립트에서 오류를 줄이고 코드의 안전성을 높일 수 있는 장점이 있다!

2. axios interceptor

api에서 헤더에 토큰 넣는 코드를 반복적으로 쓰다가 이 중복 코드를 어떻게 처리하면 좋을까 하다 알게 된 기능이다. 근데 과제 마감 얼마 안 남고 알게 된 거라 적용해서 리팩토링 할 예정,,, -> 수정했음!

🎈axios interceptor가 뭐지?

Interceptor는 Axios의 기능 중 하나. Interceptor라는 단어가 생소하게 느껴질 수 있는데 간단히 말하면 중간에 끼어들어서 원하는 작업을 해주는 도우미 라고 생각하면 된다. 비유를 해보자면,,, 친구가 나한테 전화를 걸었다고 가정했을 때 보통은 곧바로 받겠지만 가끔은 전화를 받기 전 잠깐 무언가를 하고 (메시지로 먼저 확인을 한다던가) 다음 단계로 넘어가길 원할 때가 있을 때랑 같다고 생각하면 된다. Interceptor도 이거랑 비슷하게 요청이 서버로 가기 전이나 응답이 도착한 후에 특정 작업을 수행할 수 있도록 도와주는 역할을 함!

3. React.ChangeEvent<HTMLInputElement>

특정 타입의 입력 이벤트를 다룰 때 쓰면 유용한 기능이다. 특히 input 필드와 같은 HTML 요소에 사용자가 텍스트를 입력할 때 발생하는 이벤트를 다룰 때 유용하다.

아래처럼 input 필드의 값이 변할 때마다 발생하는 onChange 이벤트 핸들러를 만들었을 때

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  console.log(event.target.value);
};

React.ChangeEvent<HTMLInputElement>는 input 요소에서 발생하는 이벤트라고 명확하게 타입을 지정해주는 역할을 한다. 이 타입을 지정하면 event.target.value에 접근할 때 타입스크립트가 자동으로 이 값이 string 타입이라는 걸 인식하니까 추가적인 타입 체크나 변환 없이 안전하게 사용할 수 있다.

4. 타입스크립트에 대한 전반적인 이해,, 기본적인 개념들,,,

5. React.Dispatch<React.SetStateAction<string>>

이번에 상태 업데이트 함수를 다른 컴포넌트로 전달하려고 하다 보니 그 함수에 타입을 어떻게 지정할지 고민하게 되었다... 상태를 설정하는 함수인데 단순히 string 타입으로만 지정하면 안 됐었다.

그래서 찾아보니까 React의 Dispatch와 SetStateAction 타입이 있다는 걸 알게 되었다.
React.Dispatch<React.SetStateAction<string>>로 지정하면 문자열 상태를 업데이트하는 함수라는 의미를 전달할 수 있었고 덕분에 이 함수를 받는 컴포넌트에서도 상태 설정 함수라는 걸 명확히 알 수 있게 되어 타입 안전성도 확보됐다!

이 타입은 기본적인 setState 함수 타입을 React에서 직접 제공해 주기 때문에 굉장히 유용하다! 이걸 쓰면 상태를 업데이트하는 함수 타입을 간단히 선언할 수 있다. 이제 상태를 업데이트하는 함수가 필요할 때마다 이 친구를 써주면 훨씬 수월해질 듯,, 👍🏻

❓ 구현 과정에서의 어려웠던/고민했던 부분

1. 타스 초기 세팅

처음에 vite로 타입스크립트를 설정하고 나니,,, 진심 config 파일이나 App.tsx에서 에러가 엄청 많이 떴다. 근데 아무리 찾아봐도 해결이 안 되길래 (에러 하나 해결하면 다른 데서 에러 10개 남) 시작 전부터 살짝 포기할 뻔 했다... ㅎㅎ 근데 다행히 https://velog.io/@otterji/Vite-tsconfig-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0 이런 글을 본 후에 vscode를 업데이트 해봤더니 에러가 안 났다! 물론 vscode 업데이트 하는 법도 헤매서 오래 걸리긴 했지만 이런 원인일 거라고는 상상도 못했다.

2. 내 취미 조회 useEffect 안 쓰고 어떤 방법으로 구현해야할까?

useEffect를 쓰지 않는 게 맞다고 해서 내 취미 조회할 때 useEffect를 쓰지 않으려고 했는데 그럼 어떤 방식으로 대체해서 구현해야할지 감이 안 와요.. 다들 이 부분에 대해 어떻게 구현하셨는지 궁금해요

3. 폴더 구조 및 컴포넌트 분리

이건 과제할 때마다 어떻게 컴포넌트나 훅들을 분리해야 할지 폴더 구조는 어떻게 하는 게 맞는 건지에 대해 오래 고민하게 되다보니 항상 궁금해지는 부분인 것 같습니다... 단일 책임 원칙에 최대한 따르려고 노력했는데 아직 부족한 거 같아요 ㅜㅜ 다들 어떻게 하셨는지 궁금합니다! 공통 컴포넌트를 잘 분리 못한 거 같기도 하고,, 유효성 검사 로직 같은 걸 유틸로 분리하는 게 좋을지 고민이네용



🥲 소요 시간

  • 18h

🖼️ 구현 결과물

회원가입

default.mov

로그인

default.mov

취미

default.mov

내 정보

default.mov

로그아웃

default.mov

@daahyunk daahyunk changed the title init: 프로젝트 초기 설정 [4주차 기본/심화/공유 과제] API 통신 Nov 12, 2024
Copy link

@shroqkf shroqkf left a comment

Choose a reason for hiding this comment

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

React와 TypeScript를 활용해서 과제 잘해주신 것 같습니다!! 컴포넌트 구조와 상태 관리가 잘 되어있어 전반적으로 가독성, 유지보수성 모두 잘 고려한 코드로 과제하느라 고생하셨습니다 🫶

Copy link

Choose a reason for hiding this comment

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

'getMyHobby' 와 'getOtherUserHobby' 두 함수 모두 API 호출 로직이 비슷한 거 같아요! 이를 하나의 공통 유틸리티 함수로 추출하여 재사용성을 높일 수 있지 않을까,, 싶습니당

@@ -0,0 +1,140 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { AiFillEye, AiFillEyeInvisible } from 'react-icons/ai';
Copy link

Choose a reason for hiding this comment

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

AiFillEye와 AiFillEyeInvisible 아이콘을 사용해서 비밀번호 보이기/숨기기 심화 기능까지 구현하다니!! 🤩

<Routes>
<Route path="hobby" element={<HobbyPage />} />
<Route path="info" element={<InfoPage />} />
<Route path="*" element={<Navigate to="hobby" replace />} />
Copy link

Choose a reason for hiding this comment

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

오옹 존재하지 않는 경로로 접근 시 hobby로 리디렉션하도록 처리한 거 맞을까요??

Copy link

@Rinakk Rinakk left a comment

Choose a reason for hiding this comment

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

역시 코드 뿐만 아니라 디자인 까지도 너무 예쁘네요 .. 🥹 저도 코드 뿐만 아니라 디자인 까지 혼자 잘 해낼 수 있을 때 까지 달려볼게요 ~~..!! 4주차 과제 너무 수고 많으셨고 합세도 잘 부탁드려요 🙌🖤

Copy link

@thisishwarang thisishwarang left a comment

Choose a reason for hiding this comment

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

너무 코드가 깔끔하고 과제를 잘하셔서 할 말이 없네요 ㄷㄷ

import InfoPage from '../components/MyPage/Info';

const MyPageRoutes = () => {
return (

Choose a reason for hiding this comment

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

라우터 설정은 요즘 createBrowserRouter를 사용하면 코드의 확장성과 유지보수에 더 좋습니다! 왜냐하면 createBrowserRouter는 객체방식으로 라우터를 정의하여 별도 파일로 분리하거나 재사용하기 쉬운 구조를 제공하기 때문입니다.

Comment on lines +64 to +68
const renderStep = () => {
if (step === 1) return <NameInput onNext={nextStep} setUsername={setUsername} />;
if (step === 2) return <PasswordInput onNext={nextStep} setPassword={setPassword} password={password} />;
if (step === 3) return <HobbyInput onNext={handleSignUp} hobby={hobby} setHobby={setHobby} />;
};

Choose a reason for hiding this comment

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

요런 함수들을 커스텀훅으로 분리해보는건 어떨까요?

Copy link
Member

@gudusol gudusol left a comment

Choose a reason for hiding this comment

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

로직 분리 하려고 노력하신 것도 보이고, 함수화도 잘 시키셧고, 스타일도 잘 입히신 것 같고
전반적으로 깔끔하게 잘 하신 것 같아요~
고생하셨습니다! 👍👍👍

Copy link
Member

Choose a reason for hiding this comment

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

요 파일은 npm install을 했을 때 생기는 파일로 알고 있는데, 명령어를 잘못 입력하셨던걸까요 ㅠ 저희는 yarn 을 사용해서 이 파일이 아니라, yarn.lock 파일이 생겨야 하는게 맞습니다...!

아니면 혹시 yarn이 아니라 npm을 사용하고 계실까요?

Comment on lines +13 to +19
<Router>
<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="/signup" element={<SignUpPage />} />
<Route path="/mypage/*" element={<MyPage />} />
</Routes>
</Router>
Copy link
Member

Choose a reason for hiding this comment

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

<BrowserRouter>를 사용해서 라우팅을 구현해주셨네요!
그런데, 이 방식 대신 createBrowserRouter를 사용하는 것을 더 추천드리고 싶어요!

결론부터 말씀드리면, createBrowserRouter는 라우트 설정을 보다 명시적이고 구조적으로 작성할 수 있도록 도와주는 최신 API입니다.
또한, 코드가 더 직관적이고 관리하기 쉬워지는 장점이 있어요.

문제점

  1. 라우트 설정의 명시성 부족
    <BrowserRouter> 방식은 라우트와 컴포넌트들을 JSX 안에서 설정해야 하기 때문에, 라우트 구조가 복잡해질수록 관리가 어려워질 수 있습니다.

  2. 코드의 확장성과 유지보수
    createBrowserRouter는 라우트를 객체 형태로 정의하여 별도 파일로 분리하거나 재사용하기 쉬운 구조를 제공합니다. 이런 방식은 코드 확장성과 유지보수에 유리합니다.

따라서, createBrowserRouter를 활용하면 아래와 같은 방식으로 작성할 수 있어요:

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Home />,
    children: [
      { path: 'about', element: <About /> },
      { path: 'contact', element: <Contact /> },
    ],
  },
  {
    path: '/signup',
    element: <SignUpPage />,
  },
]);

<RouterProvider router={router} />;

위와 같은 방식은 라우트 구성과 UI 구성이 분리되기 때문에 코드의 가독성과 확장성이 더 좋아집니다!

관련 공식 문서 한 번 읽어보시는 것도 추천드려요 😊

Copy link
Member

Choose a reason for hiding this comment

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

전체 페이지에 대한 라우팅은 App.tsx에서 이뤄지고 있고,
MyPage에 대한 라우팅은 /routes/MyPageRoutes.tsx에서 이뤄지고 있는데,

라우팅을 하는 로직이 여러 파일에 분산되어 있으면 유지보수가 어렵지 않을까요...?
파일 분리는 하더라도, 적어도 같은 폴더에서 하는 것이 좋다고 생각하는데, 다현님은 어떻게 생각하시나요

Comment on lines +4 to +12
const HeaderContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 2rem;
background-color: ${({ theme }) => theme.colors.primary};
color: ${({ theme }) => theme.colors.white};
width: 100%;
`;
Copy link
Member

Choose a reason for hiding this comment

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

HeaderContainer라면 div태그보단 header태그가 더 적절할 것 같아요!

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

Successfully merging this pull request may close these issues.

5 participants