원티드 프리온보딩 인턴십 후기 (1)

myung hun kang·2023년 3월 26일
1

프리온보딩 인턴십

원티드에서 진행하는 프리온보딩 인턴십의 기본 교육 과정을 마무리했다.
아직 커리어 코칭 기간이 남아있지만

지난 4~5주간 교육을 듣고 기업과제를 수행하면서 느겼던 그리고 공부했던 것들을 정리하는 시간을 가져볼까 한다.

💻 첫 주 수업 팀 빌딩

수업

첫 시간에는

  • 간단한 수업 오리엔테이션
  • 협업 프로젝트를 수행하기 위한 lint prettier 설정
  • 그리고 Github Action을 이용한 CI/CD 구축법

에 대해서 배웠다.

lint 와 prettier 설정에 대해서는 지난 프로젝트를 통해 학습을 한 상태였지만 그때 프로젝트 셋업을 위해 한 번 한 것이기 때문에 이번에 배운 내용을 토대로 (설정하는법 + vsCode 좀 더 잘 다루기)를 할 수 있게 된 것 같다.

+ 추가적으로 이와 관련하여 project starter pack을 만들어 볼 수 있어 좋았다.

팀 빌딩

팀은 이미 짜여져있었다. 9명의 인원이 함께 했는데 그중 처음부터 안계신 분이 1명 있었다.

뭐 8명도 충분히 각자 다 다른 코드를 작성하기에 오히려 9명보다 좋다고 생각했다.

팀원끼리 디스코드를 통해 1차 만남을 가지고 과제를 위해 몇차례 더 회의를 가지며 노션에 팀 페이지를 만들었다.

그리고 내가 팀장이 되었다.
지난 번에 이어 또 팀장을 했다. 처음에는 하고 싶은 생각이 별로 없었는데 하고나니 인턴십에 대해 좀 더 진지하고 적극적인 자세로 임할 수 있게 해준것 같아 잘했다고 생각이 든다.

마지막으로 끝나기전 과제에 대한 설명이 있었다.

과제는 팀으로 하나의 Best Practice를 만들어 제출하는 것!

대놓고 협업 능력을 기르기위한 과제라고 생각을 했다.

팀원들과 큰 트러블 없이 잘 구현을 마무리하는 것이 팀리드로서의 목표이다!

이미 끝나고 후기를 쓰는 입장에서 이야기를 한다면 큰 무리없이 모든 과제를 잘 마무리 했다. 🙂

🕺과제 1 - todo list

인턴십에 참여하기 위해서는 제출해야하는 과제가 있었다.
그중 프론트엔드 지원자는 login 기능과 간단한 todo list를 구현하는 과제를 수행해야했다.

사전과제 링크

사전과제

📝 과제에서 중요하게 생각한 부분

Signup - login - logout

login 기능은 어떤 과제를 수행해도 한번쯤은 만나게 되는 것 같다.
그만큼 당연히 필요한 기능이면서 중요하고 보안에 신경을 써야하는 기능이다.

과제에서 원하는 구현내용은 다음과 같다.

  • signup & login 시 조건에 맞는 email , password 유효성 검사
  • signup 시 login 페이지로 이동
  • login 시 서버로부터 JWT 받아서 localstorage에 저장
  • login 시 authentication 관련 라우트 접근시 todo 라우트로 자동 이동
  • 반대로 login 안됐을 시 todo 라우트 접근 제한

Authentication은 많이 구현을 해왔던 터라 크게 부담이 없었다.

하지만 라우팅 처리에서 조금 고심을 했다.

login 상태를 localstorage의 토큰 존재여부로 판단하고 각 페이지 접근 시 제한을 두는 식으로 구현

Private Route

로그인 시 todo 라우트로의 접근 제한은 따로 Private Route를 만들어서 제한을 뒀다.


const PrivateRoute = () => {
  const { isLoggedIn } = useContext(AuthContext);
  const { setUserToken } = useContext(TodoContext);

  useEffect(() => {
    if (isLoggedIn) {
      const userToken = StorageControl.storageGetter("token");
      if (!userToken) return;
      setUserToken(userToken);
    }
  }, [setUserToken, isLoggedIn]);

  return isLoggedIn ? <Outlet /> : <Navigate to="/signin" />;
};

export default PrivateRoute;

BUT

로그인 후 login 관련 라우트를 막는데 문제가 있었다.

login , signup page 컴포넌트 내에서 useEffect로 라우트 접근 가능여부를 판단하고 제한을 두려한것

이렇게 하면 어쩔 수 없이 login 과 signup 컴포넌트에 일단 들어가야하고 그렇게 되면 아주 찰나의 시간이지만 유저에게 login 과 signup 컴포넌트의 UI가 보이게 된다.

안좋은 UX를 주게 된다

위 Private Route 처럼 라우트 단에서 미리 판단하고 경로를 바꿔줘야 이런 문제가 생기지않는다. 현재 팀으로 제출한

여기서 Private Route 처럼 login PrivateRoute를 만들면 되긴하지만 비슷한 코드가 반복되니 좋은 구조라고 보기 힘들 것 같다.

횡단 관심사

routing 관련해서 횡단 관심사를 엮어서 생각할 수 있다.

로그인 여부가 전체 라우트를 좌지우지 하고 있으니 전체 라우트를 관리하는 router 컴포넌트를 만들어서 거기서 login 여부에 따를 route 처리를 해주면 되겠다.

react-router-dom의 createBrowserRouter 로 path별 router를 설정하여 route 처리를 다루면 될 듯 하다

 아직 구현하지는 않았지만 조만간 구현후 글을 작성하도록 하겠다. 

✔️ Todo list 와 Todo CRUD

과제에서 구현하기를 바랐던 todo list의 기능은 다음과 같다.

  • 서버 api 통신으로 todo list 받아오기
  • input 창을 통해 todo 작성해 list에 추가
  • todo list 에 수정 삭제 버튼을 이용 수정 삭제
  • todo list의 check box로 완료여부 선택

서버와의 통신으로 todo list를 받아와 화면에 보여주고 CRUD 기능을 구현하라는 과제이다.

구현에는 큰 문제가 없었다.

하지만...

코드 관심사 분리

팀원들과 회의를 통해 구현한 Best Practice에는

  • api 통신 부분
  • axios 설정 부분

에 대한 분리는 나름 잘 되었다고 생각한다.

// utils axios.ts 

const axiosConfig: AxiosRequestConfig = {
  baseURL: baseURL,
  timeout: 180000,
  withCredentials: false,
  headers: {
    'Content-Type': 'application/json',
  },
}

export const client = axios.create(axiosConfig)

client.interceptors.request.use(config => {
  if (!config.headers) return config

  const token: string | null = localStorage.getItem('ACCESS_TOKEN')
  if (token !== null) {
    config.headers.Authorization = `Bearer ${token}`
  }

  return config
})

... 생략
// todo-api.ts 중

import { TodoDto, UpdateTodoDto } from './dtos/TodoDto'
import { client } from '../utils/axios'

export function fetchTodos() {
  return client.get('/todos')
}

export function createTodo(todo: TodoDto) {
  return client.post('/todos', todo)
}

... 생략

하지만 todo list CRUD 통신을 위한 코드가 컴포넌트에 그대로 구현되어 있다.

function Todo() {
  const [todoInput, setTodoInput] = useState('')
  const navigate = useNavigate()
  const [todos, setTodos] = useState<Itodos[]>([])
  const ACCESS_TOKEN = localStorage.getItem('ACCESS_TOKEN')

  useEffect(() => {
    if (!ACCESS_TOKEN) {
      navigate('/signin')
    } else {
      getTodos()
    }
  }, [])

  const getTodos = async () => {
    const response = await fetchTodos()
    setTodos(response.data)
  }

  const createTodoHandler = async (todo: string) => {
    const response = await createTodo({ todo })
    if (response.status === 201) {
      setTodoInput('')
      setTodos([response.data, ...todos])
    }
  }

 ... 중략
  
 return (... JSX 코드 ) 
 }

코드 관심사 분리에 대해서 다시 생각을 해보니

위 getTodos , createTodoHandler 와 같은 함수의 통신부는 가리는 편이 좋지 않았나 싶다.

커스텀 훅으로 빼도 좋고 아니면 context.api 를 써도 좋지 않았을까? 싶다.

하지만 과제를 바라보는 시점에서 따라서 다를 수 있을 것 같다.
단순히 구현만 생각한다면 현재도 좋은 코드라고 생각한다. 하지만 관심사를 분리하여 컴포넌트에서는 api 통신부는 신경쓰지 않도록 한다면 앞서 말한대로 따로 빼는 것이 좋을 것 같다.

여튼 이래저래 첫 과제를 잘 마무리했다.

여러 팀원들간의 의견 조율과 페어 코딩 및 코드 스타일 맞추기 등등 할게 생각보다 많았다.


후기를 이번 하나로 마무리를 하려했는데 길어질 것 같다... ㅎㅎ
빠르게 다음 후기를 작성하도록 하겠다.

다음부터는 주차별 과제와 학습내용을 중점으로 작성하도록 하겠다.





끄 읕 ~~~

profile
프론트엔드 개발자입니다.

0개의 댓글