Skip to content

기술 스택

장우석 edited this page Dec 18, 2022 · 7 revisions

🛠️ 기술 스택

image

공통

Typescript

  • 동적 타입 언어인 Javascript는 유연하지만 프로젝트의 규모가 커질수록 예측할 수 없는 에러를 발생시킨다.
  • 따라서 타입 체킹을 통해 정적 타입 언어의 장점인 컴파일 시 오류 검사를 위해 Typescript를 사용한다.
  • 또한 매개변수에 타입이 지정되므로 IDE에서 자동 완성 지원이 편리하고 생산성에 이점을 가져다 준다.
  • Interface, Decorator 등 편리한 문법을 지원하여 객체 지향의 장점을 가져갈 수 있다.

프론트엔드

Next.js

  • 개발하려는 서비스의 핵심 비즈니스 로직이 사용자 정보제공이기 때문에 검색엔진 최적화(SEO)를 위해 SSR 프레임워크인 Next.js를 사용하기로 하였다.
  • 빌드 타임 시 HTML을 생성하여(SSG) 제공할 수 있으므로 사용자 입장에서 느껴지는 초기 페이지 로딩 속도를 크게 줄일 수 있다.
  • 라우팅이 react를 사용했을 때 보다 편리하다. react-router-dom같은 라이브러리를 사용하지 않고 폴더 구조로 라우팅을 편하게 할 수 있다.

TanStack Query

  • 클라이언트에서 전역으로 관리해야할 상태가 많지 않다.

  • Redux, Mobx, Recoil과 같은 상태관리 도구는 클라이언트 쪽 상태를 관리하기에는 적합하나 서버의 데이터들을 관리하기에는 적합하지 않다. 클라이언트 상태관리 도구에서 서버의 상태를 fetch해서 함께 관리하면 클라이언트 상태와 서버 상태가 섞여 구조가 복잡해진다.

    두 개의 상태가 묶이면서 서버 데이터를 위한 로직이 과도하게 커지고, 클라이언트 상태를 관리하는 것이 아닌 서버상태를 관리하는 것과 같은 모습이 된다.

    서버상태관리 도구를 사용한다면 서버와 클라이언트 데이터를 분리할 수 있다.

  • 제공해야할 대부분의 정보는 서버에서 가져와야하고 순위 시스템의 경우 최신화가 중요하기 때문에 서버 데이터를 효율적으로 캐싱하고 데이터를 지속적으로 동기화하고 업데이트하는 작업을 도와주는 서버상태관리 도구를 도입하기로 하였다.

  • 서버상태관리 도구를 사용하지 않았을 때 비동기 처리를 할 경우 loading, error state들이 비대하게 늘어나 코드의 관심사 분리가 힘들고 지저분해지는 경우가 있는데 직관적이고 짧은 코드로 대체할 수 있게 된다.

    아래는 Redux로 서버의 데이터를 비동기적으로 받아오는 로직을 작성했을 때이다.

    import React, { useEffect } from "react";
    import { useSelector, useDispatch } from "react-redux";
    import axios from 'axios';
    
    const SET_TODOS = "SET_TODOS";
    
    export const rootReducer = (state = { todos: [] }, action) => {
      switch (action.type) {
        case SET_TODOS:
          return { ...state, todos: action.payload };
        default:
          return state;
      }
    };
    
    export const App = () => {
      const todos = useSelector((state) => state.todos);
      const dispatch = useDispatch();
    
      useEffect(() => {
        const fetchPosts = async () => {
          const { data } = await axios.get("/api/todos");
          dispatch({
            type: SET_TODOS,
            payload: data}
          );
        };
    
        fetchPosts();
      }, []);
    
      return (
        <ul>{todos.length > 0 && todos.map((todo) => <li>{todo.text}</li>)}</ul>
      );
    };

    아래는 React-query로 작성한 코드이다. 두 코드의 길이를 비교한다면 확연한 차이를 볼 수 있다.

    import React from "react";
    import { useQuery } from "react-query";
    import axios from "axios";
    
    // 캐싱 설정을 어느곳에서든 정의할 수 있습니다
    const fetchTodos = () => {
      const { data } = axios.get("/api/todos");
      // 필요하다면 데이터 유효성 검사를 여기서 수행하여, 실제로 값을 사용하는 곳에서는 검증된 값을 사용할 수 있습니다
      return data;
    };
    
    const App = () => {
      // 데이터가 필요한 곳에서 호출하면 됩니다
      const { data } = useQuery("todos", fetchTodos);
    
      return data ? (
        <ul>{data.length > 0 && data.map((todo) => <li>{todo.text}</li>)}</ul>
      ) : null;
    };
  • 서버상태 관리 도구에 서버 데이터 페칭과 캐싱 역할을 위임하고 다른 기능 구현에 집중할 수 있다.

  • 별도의 설정 없이 즉시 사용할 수 있고 react-hook과 같은 구조로 사용할 수 있어 사용방법이 쉽다.

  • 이외에도 같은 데이터에 대한 요청이 여러번 있을 경우 중복을 제거한다.

  • 백그라운드 상에서 데이터를 최신화 시켜준다.

  • 공식문서가 잘돼있다.

StoryBook

  • 디자인 시스템의 도입을 통해 일관성있는 디자인을 유지할 수 있다.
  • 미리 개발된 UI들을 재활용하여 생산성이 올라간다.
  • 협업하는데 있어 프론트엔드 UI를 문서화하여 유지보수와 관리의 용이성이 생긴다고 판단했다. 개발자끼리 서로의 코드를 컴포넌트 단위로 명확하게 이해할 수 있게 도와줌으로써 생산성의 향상을 이끌어 낼 수 있다.
  • 스토리북을 통해 개발을 하게 되면 컴포넌트의 재사용성을 더욱 고려하면서 설계하게 된다.
  • 페이지가 완성되기 전에도 결과물을 컴포넌트 단위로 불 수 있고 이러한 장점으로 인해 컴포넌트단위로 피드백을 하면서 개선해 나갈 수 있다
  • 각 컴포넌트에 대해 상태에 따른 변화를 스토리북에서 쉽게 파악하고 관리할 수 있다.
  • 그룹프로젝트를 하는 데 있어 협업 도구로 사용해볼 기회가 된다고 생각했다.

Styled Components

  • Styled-components를 사용했을 때 CSS-in-JS이므로 리액트에서 컴포넌트 기반 개발을 할 때 각 컴포넌트의 스타일을 명시적으로 파악하기 쉽다.
  • Style을 각 컴포넌트에서 작성하기 때문에 네이밍 중복을 고려하지 않아도 된다.
  • 클래스명을 사용할 필요가 없기 때문에 클래스명을 짓는데 소요되는 코스트가 적다.
  • css파일을 사용할 경우 파일의 수가 비대하게 늘어나고 파일구조가 복잡해진다. styled-component를 사용하면 따로 유지보수해야 할 스타일 시트 파일을 제거할 수 있다.
  • props를 활용한 조건부 스타일링이 가능하다.
  • styled-components를 사용하면 js파일이 너무 커질 수 있다하는데 이건 컴포넌트를 잘 분리한다면 해결되는 문제라 생각한다.
  • 하나의 style을 가진 component를 작성해 재사용성을 높일 수 있다.
  • Scss 라이브러리 설치 없이 Scss 문법을 사용할 수 있습니다.
  • Styled-component로 만든 컴포넌트에 style을 오버라이드 해서 사용할 수 있다. 재사용성 증가.

⚙️ 백엔드

NestJS

  • 관련한 미들웨어를 모두 구현해야하는 Express에 비해 프레임워크가 내장하고 있는 자체 기능들이 편의성을 많이 제공해준다. 따라서 비지니스 로직에 집중이 가능하다.
  • 프레임워크가 제한해주는 구조가 협업 시 코드 파악을 용이하게 해준다.
  • 타입스크립트만 지원하기 때문에 객체지향이 제공하는 장점을 최대한 활용할 수 있다.

Redis

  • 인메모리 DB로 디스크I/O에 비해 병목이 적다.
  • API 캐싱 및 refresh token 저장을 위해 사용한다.

MongoDB

  • 필요한 데이터 사이의 관계가 복잡하지 않다. 따라서 관계에 대한 무결성 검사의 필요성 보다는 성능적인 측면에 초점을 맞춰도 된다고 판단했다.
  • 트랜잭션을 통해 ACID 보장이 RDBMS와 동일하게 보장이 가능하다.
  • 차후에 replication을 통한 수평 확장의 가능성이 RDBMS보다 열려있다.

Mongoose

  • MongoDB와 가장 호환이 잘 되는 ORM 프레임워크라 판단했다.
  • TypeORM은 MongoDB 버전에 따라 호환성 이슈가 존재한다.
  • Prisma는 GraphQL과의 호환성을 강조해서, 굳이 안전성이 입증된 Mongoose의 대안으로 선택할 필요성을 느끼지 못했다.

AdminJS

  • 어플리케이션의 컨텐츠들을 수정할 수 있는 Admin UI를 제공한다.
  • NestJS와 Mongoose와 쉽게 통합이 가능하다.
  • 개발자가 아닌 운영자도 이를 통해 컨텐츠들을 수정할 수 있으므로, 서비스 유지보수 측면에서 이점이 크다.

Jest

  • 팀원 모두가 테스트 프레임워크로 Jest를 사용했던 경험이 있다.
  • 평소 쓰던 도구에 대한 익숙함에서 오는 생산성, 다른 프레임워크를 학습하는 데 드는 비용을 감수하면서 굳이 다른 테스트 프레임워크를 선택할 필요성을 느낄 수 없었다.

🏗 인프라

nCloud

  • 부스트캠프 자체 크레딧 지원을 통해 비용을 절감할 수 있고 클라우드 서비스 별로 큰 차이가 있는 게 아니여서 사용하기로 결정했다.

Nginx

  • 요청 당 스레드 방식인 Apache는 요청 당 스레드를 할당하는 방식이므로 10K 문제를 해결하기 힘들다.
  • 그에 비해 이벤트 루프 방식의 Nginx는 자원의 효율성, 더 많은 트래픽 처리에 이점이 있다.
  • reverse proxy, 로드밸런싱, 캐싱 등 다양한 기능을 지원한다.

Docker / Docker Compose / Docker Swarm

  • 독립된 환경을 보장 할 수 있어 개발 환경과 배포 환경을 통합 할 수 있다.

Github Actions

  • 저장소로 Github를 사용하면서 PR, Project, Issue와 같은 기능을 적극적으로 활용하면서 CI에서도 이를 활용 할 수 있다.
  • Jenkins보다 쉽게 설정이 가능하다.

🚀 Devrank

🏠 Home

팀 소개

👨‍👩‍👧‍👦 팀원

팀 문화

🤝 그라운드 룰
⌨️ 컨벤션
🌳 브랜치 전략

개발 문서

🎨 피그마
📚 기획서
📜 Backlog
🛠️ 기술스택
📒 API 명세
📝 Dev log

데일리 스크럼

💬 데일리 스크럼

스프린트 계획 회의

🏃 Week2
🏃 Week3
🏃 Week4
🏃 Week5
🏃 Week6

멘토링

👨‍🏫 Week1
👨‍🏫 Week2
👨‍🏫 Week3
👨‍🏫 Week4
👨‍🏫 Week5

회고

✒️ Week1
✒️ Week2
✒️ Week3
✒️ Week4
✒️ Week5
✒️ Week6

Clone this wiki locally