본문 바로가기

archive/Project

[Project No.1] 원티드 4월 프리온보딩 인턴십 사전과제 Part 1.

오늘의 과제

1. 과제 요구사항 분석

2. CRA

3. 레퍼런스 및 소스코드 살펴보기 (이럴 여유가 없었다..)

4. 컴포넌트 틀 만들기 가능하면 회원가입 기능 구현 시작하기(블로그에 상세하게 작성하지 못하고 제출함)

 

 

 

#1 과제 요구사항 분석

 

(1) 사전과제 제출관련사항

 

1) 사전 과제 : 레포지토리 명칭

  • 사전 과제 풀이 후 Github 링크를 제출해 주세요.(Repository명은“wanted-pre-onboarding-frontend”으로 생성)

2) 이력서 : 

  • 원티드 이력서를 작성해 주세요.
  • 오른쪽 상단에서 이력서 다운로드 버튼을 눌러주세요.
  • 다운로드한 PDF 파일을 본인 구글 드라이브에 업로드해 주세요.
  • 공유 > '링크가 있는 모든 사용자'에게 뷰어 권한을 설정하여 링크를 복사해 주세요.
  • 참가신청 시 링크를 제출해 주세요.

 

(2) 사전과제 전반적 안내사항

1) 안내 사항

  • 기능의 정상 동작 여부, 작성된 코드의 퀄리티, Git 관리 수준 등을 기준으로 평가
  • 이 레파지토리에서 제공된 API를 활용해서 선발 과제를 진행
  • 과제 제출은 참가 신청시 수행한 과제의 레파지토리의 주소를 제출

2) 주의 사항

  • 레파지토리를 들어갔을 때 바로 소스코드가 보이도록 해주세요, 불필요한 depth가 존재하면 안됩니다.
  • 레파지토리 안의 특정 폴더의 링크가 아닌 레파지토리의 링크를 제출해주세요
  • 제출시기 가산점은 모집 페이지에서 제출한 시기와, 레파지토리의 default branch의 최종 커밋 시기를 기반으로 결정됩니다.
  • 과제 제출 후에는 코드 변경을 지양해주시고, 평가와 무관하게 수정을 하고 싶을 경우 default branch(master or main)가 아닌 별도의 브랜치에서 작업해주세요

3) 진행가이드

  • Create React App을 이용해 과제를 구현
  • git clone 후, npm install & npm start 명령어를 통해서 바로 정상동작이 가능하게 해주세요
    • clone 후, 별도의 파일을 생성하거나 환경변수를 수동으로 설정하는 등 추가적인 과정 없이 바로 실행이 가능하도록 해주세요
  • 함수 컴포넌트를 이용해서 진행해주세요
  • UI는 지원자 개인이 생각했을 때 자연스러운 형태로 구현해주세요, UI는 평가에 영향을 미치지 않습니다.
  • README.md 작성은 필수입니다. 아래의 사항은 반드시 포함되도록 해주세요
    • 프로젝트의 실행 방법
    • 데모 영상
    • 데모 영상은 배포 링크로 대체 가능하며, 배포가 되었고 배포된 사이트에서 기능이 모두 동작하면 가산점이 부여됩니다.
    • 배포된 사이트에서 기능이 정상동작 하지 않는다면 배포 가산점이 부여되지 않습니다
      • 기능이 정상 동작하지 않는 예시:
        • 새로고침하면 404 에러 페이지 표출
        • "/" URL이 아닌 "/signup"등의 경로로 바로 접속할 경우 404 에러 페이지 표출 등
  • 기능구현에 직접적으로 연관된 라이브러리 사용은 허용되지 않습니다.(React-Query 등)
  • 사용가능한 라이브러리 목록은 아래와 같습니다.
    • React Router
    • HTTP Client 라이브러리(Axios 등)
    • 스타일링 관련 라이브러리(Sass, Styled Components, Emotion, tailwind 등)
    • 아이콘 등 UI 관련 라이브러리(Font-Awesome, React-Icons, Bootstrap 등)
    • 기능과 직접적인 연관이 없는 설정관련 라이브러리(craco, dotenv 등)

(3) 사전과제 구현관련

1) 로그인 / 회원가입

  • /signup 경로에 회원가입 기능을 개발해주세요
  • /signin 경로에 로그인 기능을 개발해주세요
    • 페이지 안에 이메일 input, 비밀번호 input, 제출 button이 포함된 형태로 구성해주세요
      • 이메일 input에 data-testid="email-input" 속성을 부여해주세요
      • 패스워드 input에 data-testid="password-input" 속성을 부여해주세요
      • 회원가입 button에 data-testid="signup-button" 속성을 부여해주세요
      • 로그인 button에 data-testid="signin-button" 속성을 부여해주세요
<!-- 예시 -->
<input data-testid="email-input" />
<input data-testid="password-input" />
<button data-testid="signup-button">회원가입</button>

Assignment 1

  • 회원가입과 로그인 페이지에 이메일과 비밀번호의 유효성 검사기능을 구현해주세요
    • 이메일 조건: @ 포함
    • 비밀번호 조건: 8자 이상
    • 이메일과 비밀번호의 유효성 검사 조건은 별도의 추가 조건 부여 없이 위의 조건대로만 진행해주세요 (e.g. 비밀번호 유효성 검사에 특수문자 포함 등의 새로운 조건을 추가하는 행위, 비밀번호 확인 조건을 추가하는 행위 등은 지양해주세요)
  • 입력된 이메일과 비밀번호가 유효성 검사를 통과하지 못한다면 button에 disabled 속성을 부여해주세요
  • 보안 상 실제 사용하고 계신 이메일과 패스워드말고 테스트용 이메일, 패스워드 사용을 권장드립니다.

Assignment 2

  • 회원가입 페이지에서 버튼을 클릭 시 회원가입을 진행하고 회원가입이 정상적으로 완료되었을 시 /signin 경로로 이동해주세요

Assignment 3

  • 로그인 페이지에서 버튼을 클릭 시, 로그인을 진행하고 로그인이 정상적으로 완료되었을 시 /todo 경로로 이동해주세요
    • 로그인 API는 로그인이 성공했을 시 Response Body에 JWT를 포함해서 응답합니다.
    • 응답받은 JWT는 로컬 스토리지에 저장해주세요

Assignment 4

  • 로그인 여부에 따른 리다이렉트 처리를 구현해주세요
    • 로컬 스토리지에 토큰이 있는 상태로 /signin 또는 /signup 페이지에 접속한다면 /todo 경로로 리다이렉트 시켜주세요
    • 로컬 스토리지에 토큰이 없는 상태로 /todo페이지에 접속한다면 /signin 경로로 리다이렉트 시켜주세요

 

2) TODO LIST

Assignment 5

  • /todo경로에 접속하면 투두 리스트의 목록을 볼 수 있도록 해주세요
  • 목록에서는 TODO의 내용과 완료 여부가 표시되어야 합니다.
  • TODO의 완료 여부는 <input type="checkbox" />를 통해 표현해주세요
  • TODO는 <li> tag를 이용해 감싸주세요
<li>
  <label>
    <input type="checkbox" />
    <span>TODO 1</span>
  </label>
</li>
<li>
  <label>
    <input type="checkbox" />
    <span>TODO 2</span>
  </label>
</li>

 

Assignment 6

  • 리스트 페이지에 새로운 TODO를 입력할 수 있는 input과 추가 button을 만들어주세요
    • TODO 입력 input에는 data-testid="new-todo-input" 속성을 부여해주세요
    • TODO 추가 button에는 data-testid="new-todo-add-button" 속성을 부여해주세요
<input data-testid="new-todo-input" />
<button data-testid="new-todo-add-button">추가</button>
  • 추가 button을 클릭하면 입력 input의 내용이 새로운 TODO로 추가되도록 해주세요
  • TODO를 추가 한 뒤 새로고침을 해도 추가한 TODO가 목록에 보여야 합니다.

Assignment 7

  • TODO의 체크박스를 통해 완료 여부를 수정할 수 있도록 해주세요.

Assignment 8

  • TODO 우측에 수정버튼과 삭제 버튼을 만들어주세요
    • 수정 버튼에는 data-testid="modify-button" 속성을 부여해주세요
    • 삭제 버튼에는 data-testid="delete-button" 속성을 부여해주세요
<li>
  <label>
    <input type="checkbox" />
    <span>TODO 1</span>
  </label>
  <button data-testid="modify-button">수정</button>
  <button data-testid="delete-button">삭제</button>
</li>

Assignment 9

  • 투두 리스트의 삭제 기능을 구현해주세요
    • 투두 리스트의 TODO 우측의 삭제버튼을 누르면 해당 아이템이 삭제되도록 해주세요

Assignment 10

  • 투두 리스트의 수정 기능을 구현해주세요
    • TODO 우측의 수정 버튼을 누르면 수정모드가 활성화 되도록 해주세요
    • 수정모드에서는 TODO의 내용을 변경할 수 있어야 합니다.
    • 수정모드에서는 TODO의 내용이 input창 안에 입력된 형태로 변경해주세요
      • 수정 input창에는 data-testid="modify-input" 속성을 부여해주세요
    • 수정모드에서는 TODO의 우측에 제출버튼과 취소버튼이 표시되게 해주세요
      • 제출버튼에는 data-testid="submit-button" 속성을 부여해주세요
      • 취소버튼에는 data-testid="cancel-button" 속성을 부여해주세요
    • 제출버튼을 누르면 수정한 내용을 제출해서 내용이 업데이트 될 수 있도록 해주세요
    • 취소버튼을 누르면 수정한 내용을 초기화 하고, 수정모드를 비활성화 해주세요
<input data-testid="modify-input" />
<button data-testid="submit-button">제출</button>
<button data-testid="cancel-button">취소</button>

 

 

사실 이 레포지토리에 가서 내용을 보면된다.. 내용을 요약하고 싶었는데 노션에 할일 목록을 정리해놔서 굳이 이것까지 또 텍스트를 정리해야하나 싶었다. 그 시간에 코드 한 줄이라도 짜서 가산점 제일 높은 시기안에 제출해야한다.

 

 

#2 Git repository 생성 및 CRA

먼저 지정된 이름으로 레포지토리를 생성하고, 추후에 사용법을 작성할 Readme 파일도 추가되도록 하였다.

vscode로 클론을 해와서 CRA를 해보자.

그리고, 이번에는 코드를 기능단위로 최대한 나누어서 커밋을 해보자.

일단은 성공적으로 CRA는 성공했지만, depth가 하나 더 있는 것이 맘에 들지 않는다. 이런 현상을 막으려면 상위폴더에서 CRA를 한 다음에 해당 폴더를 레포지토리에 연결하는 편이 훨씬 좋았을 것이다. 아니면 다른 좋은 방법이 있는건지는 모르겠다.

 

일단 프로젝트 시작한 기념으로 바로 커밋을 해줬다.

 

그리고 css 초기화를 해주고 커밋을 해주려고 했는데.. 그보다도 회원가입, 로그인 구현이 걱정이라 어떤식으로 구현할지 미리 구상을 해야한다. 다행히 동료와 같이 진행하기로 하여서 동료가 정리해놓은 순서에 따라 라우팅과 관련된 부분을 먼저 구현하기로 하였다. 다만, 라우팅을 하려면 컴포넌트들의 목록을 미리 짜야하는데 당장 라우팅되는 페이지만 고려할 것이 아니라 완성된 전체 그림을 고려할 때, 재사용되는 컴포넌트부터 라우팅 되는 각각의 페이지 컴포넌트까지 짜야하겠다.

 

무작정 작성하지말고 레퍼런스 코드들을 참고할 수 있는 것들을 최근에 소스들을 얻은관계로 해당 소스를 먼저 살펴보고 어떤 식으로 작성할지 계획을 먼저 세우겠다.

 

그리고 CSS는 초기화해서 사용하기보다는 일단 이번에는 코드의 양이 엄청 많아지지는 않을 것 같아 가독성을 크게 해치지 않을 것 같아서 Styled-component를 사용하되 다음 프로젝트들에서도 사용해야할지는 고민을 해보아야 하겠다.

 

#3 구현 및 제출(feat. User flow)

로그인 기능 관련하여, JWT 토큰을 API로부터 응답받아서 해당 토큰을 가지고 있는지 여부에 따라 디렉션하는 경로가 달라지기 때문에 해당 부분에 대한 구현을 중심으로 구현해야해서 교육과정중에 JWT 토큰과 관련된 실습을 한 레퍼런스 코드를 보았다.

 

레퍼런스만 살펴보고 유효성 검사, 회원가입 페이지, 로그인 페이지와 App.js에서 라우팅하는 로직까지는 구현이 가능할 것 같아 소스코드 파헤치기는 뒤로하고 일단 구현에 들어가기로 하였다..ㅎㅎ

 

그런데, 로그인 되었는지 토큰이 있는지 여부에 따라 리디렉션하는 로직이 머릿속으로만 그리기는 복잡하여 Userflow를 간단하게 그려보고 시작하려고 한다.

 

라우팅 로직을 짜려다보니 기본 경로인 "/"로 접속하였을 때 어떤 경로로 보내주어야 하지 헷갈려서 만들게 된 것인데, 만들기를 잘했다는 생각이 든다. 과제에서 요구하는 사항이 회사에서 요구하는 사항이라고 생각해보자. 미리 설계를 해놓지 않고 구현하다보면 어떤 상태에서 어떤 경로로 리디렉션해주어야하는지 꼬일 수 밖에 없다. 똑같은 회원가입, 로그인 페이지가 수억개 있겠지만 리디렉션되는 조건이나 로직은 각각 다를 것이다. 물론 지금 구현하는 것은 그래도 분기가 많이 나뉘지않고 경로의 수도 적어서 크게 문제될 것은 없다. 다만, 앞으로 회원가입,로그인 기능을 구현할 때 참고자료로 계속 사용할 수도 있고 업그레이드 해서 저장해두면 다시 안만들거나 조금만 수정해서 재사용할 수 있는 User flow를 만든 것 같아서 너무 뿌듯했다.(물론 다시는 사용하지 않을 수도 있지만..!!)

 

처음 시작페이지가 Signup인지 Signin인지 같이하는 동기들과 이야기나누다가 Signup 경로가 랜딩페이지인 것으로 변경하기도 했지만 최종적으로는 보편적인 방식인 Signin을 랜딩페이지로 정하고 회원가입 버튼을 통해 회원가입으로 이동하도록 만들었다.

 

1. 라우팅 구현

이 흐름에 따라 로직을 짜보자.

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Signin from './pages/Signin';
import Signup from './pages/Signup';
import Todo from './pages/Todo';


function App() {

  //JWT token 보유 여부에 따라 true || false
  const [isLogin,setIsLogin] = useState(false);

  return (
  <BrowserRouter>
      <Routes>
        <Route path='/' 
        render={() => (isLogin ? <Redirect to="/todo" /> : <Redirect to="/signin" />)}/>
        <Route path='/signup' 
        render={() => (isLogin ? <Redirect to="/todo" /> : <Signup />)}/>
        <Route path='/signin'
        render={() => (isLogin ? <Redirect to="/todo" /> : <Signin />)} />
        <Route path='/todo' 
        render={() => (isLogin ? <Todo /> : <Redirect to="/signin" />)}/>
      </Routes>
  </BrowserRouter>
    
  );
}

App.js에서 로그인 여부에 따라 해당 컴포넌트를 랜더링할지 리디렉션할지를 삼항연산자로 나누어주고 각각의 페이지를 import 해왔다.

 

최종제출한 코드는 아래와 같이 토큰을 가져와서 토큰여부에 따라 리다이렉팅 또는 랜더링되도록 하였다. 배포를 쉽게하려고 gh-pages를 이용하기 위해 HashRouter로 변경하였지만 최종배포에 실패하고 데모영상과 함께 제출하게 되었다.

import { HashRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useState } from 'react';
import Signin from './pages/Signin';
import Signup from './pages/Signup';
import Todo from './pages/Todo';


function App() {

  //JWT token 보유 여부에 따라 true || false
  const [isLogin,setIsLogin] = useState(false);
  const token = localStorage.getItem('token');

  return (  
  <HashRouter>
      <Routes>
        <Route path='/' 
        element={token ? <Navigate to="/todo"/> : <Navigate to="/signup"/>} />
        <Route path='/signup' 
        element={token ? <Navigate to="/todo"/> : <Signup />}/>
        <Route path='/signin'
        element={token ? <Navigate to="/todo"/> : <Signin isLogin={isLogin} setIsLogin={setIsLogin} />} />
        <Route path='/todo' 
        element={token ? <Todo /> : <Navigate to="/signin"/>}/>
      </Routes>
  </HashRouter>
    
  );
}

export default App;

 

2. 각 페이지 구현하기

아직 시험해보기에는 랜더링 되는게 아무것도 없으므로 랜더링 될 수 있도록 회원가입 페이지부터 컴포넌트들을 만들어보자..!!

 

각각의 컴포넌트를 전부 돌아보면서 리펙토링의 기반을 잡아야하는데, 이번 개발 여정은 다음과 같았다.

<Chapter 1> 무근본 Atomic design pattern 대실패, 대참사

되돌아보면 이 부분에서 시간을 뺏긴 것이 너무 아쉽다. Read.me 파일을 제대로 작성할 시간이 상대적으로 부족해졌고, 배포할 시간은 물론, 커밋을 마지막에 몰아서 한 번에 하는 아쉬운 일이 계획을 제대로 세우지못하고 한 번도 제대로 정독한 적이 없는 Atomic design pattern의 적용에 있었다. 문제는 Atomic design pattern을 적용하는 예시가 Atoms, molecules, ... 이런식으로 상위 컴포넌트를 만들어가는데 폴더이름을 그렇게 만든 것도 아니고 elements, components, pages 이렇게 만들었다. 사실 이 방식은 네이밍문제와 함께 폴더 구조가 조금 꼬이는 것 말고는 괜찮았다. 그런데, element 하나하나 components에 넣고 pages에 넣어서 랜더링을 하려고 했는데 Error가 발생하였다. 어떤 에러였는지 사전과제를 이식해서 직접 리펙토링하면서 재현할 수 있다면 꼭 재현해서 해결까지 해보도록 하겠다.

 

각 컴포넌트를 스타일드컴포넌트로 작성하고 각각의 파일에 styled-components를 import 해오긴 했지만 해당 문제인지 체크해보아야한다.

 

그래서 결론은 <div>,<input>,<header>,<main> 이런 컴포넌트를 전부 만들었다가 삭제해버렸다.. 나름의 경험으로 생각할 수도 있지만 이번 과제의 시간제한을 생각했을 땐 괜히 시간만 버린 꼴이다.

<Chapter 2> module.css와 styled components의 혼용

styled components로 컴포넌트를 하나하나 다 만들려고하니 이것도 스트레스를 받아서 module.css를 처음으로 적용해보았다. 결과적으로는 styled components를 일괄적용하는 것보다 더 빠르게 된 것인지는 미지수이다. 처음으로 module.css를 사용해본 경험은 남아서 좋다.

 

<Chapter 3> custom hook 사용 실패

custom hook을 만들어서 자주 사용되는 fetch나 input을 재사용하고 싶었으나 실패하였다. 사실 hook 자체의 문제가 아니었던 것 같지만 우왕좌왕하면서 반쯤 쫄아서 custom hook 사용을 포기하였다. 리펙토링과정에서는 작동되는 custom hook을 이용하여 만들어보겠다.

 

<Chapter 4> 어떻게든 복붙으로 완성

assignment 1부터 10까지 요구사항을 맞추겠다는 일념으로 코드의 효율은 1도 고려하지 않고 어떻게든 기능이 구현되도록 props를 내려주는 것에 있어서 필요성을 크게 고려하지 않았으며, state를 생성하는 것도 자꾸 만들었다 없앴다 하였다. 텍스트에 취소선 스타일 주는 것도 급하게 찾아서 적용하고 시간에 쫓기며 엉망으로 코드를 작성하였다.

 

이번 글에서는 문제점들을 다 짚어보았고 다음 글에서 리펙토링 과정을 상세하게 담아보며 가능하면 서버까지 직접 달아서 배포까지 마치도록 하겠다.

 

'archive > Project' 카테고리의 다른 글

[MainProject] day-15 extra (5/20-5/21)  (3) 2023.05.21
[MainProject] day-14,15 (5/18-5/19)  (0) 2023.05.19
[MainProject] day-13 (5/17)  (2) 2023.05.17
[MainProject] day12 (5/16)  (1) 2023.05.17
[MainProject] day 10 extra (5/13-5/14)  (1) 2023.05.15