쇼핑몰 프로젝트 (feat. React) - 프로젝트 초기 셋팅

2023. 1. 6. 18:56기록/Projects

배포 링크 : 마켓멍냥
원본 저장소 : GitHub 

두둥

그 동안은

자바스크립트 + 모듈화 + REST API 를 활용한 프로젝트만 해보았었는데,

 

이번에는 드디어 !!!!!!!

리액트 + 팀 프로젝트에 참여하게 됨 (+ 팀장!!!!)

 

팀장으로서 내가 어떤 것을 하면 좋을 지..... 생각해 보았는데...

 

1. 레포지토리/브랜치 생성

2. 프로젝트 초기 셋팅

3. 라이브러리 초기 셋팅

4. 문서화 초기 셋팅

 

이정도가 필요할 것 같았다.

 

특히 팀원들이 바로 개발을 시작할 수 있도록

빨리 프로젝트 환경 셋팅을 해놓는 것이 중요했다!!!!! (제출 기한이 정해져 있어서 급했음)

 

리액트 프로젝트를 팀 단위로 해본 것은 처음이라서

어떤 라이브러리를 써야 할 지 계속 검색해보고 ㅠㅠㅠ

팀원들과도 이야기 해보고.. 혼자 라이브러리 설치하고 삽질해보고...ㅎ;; (에러나고 ㅋ)

 

마침내....

React + Redux Toolkit + axios + scss모듈 로 정해짐

 

팀원들과 공유도 하고, 나중에 다시 참고해볼 겸 

프로젝트 셋팅 방법을 작성해 보겠음


레포지토리/브랜치 생성

  • 깃허브에서 레포지토리 생성
  • develop 브랜치 추가

 

main 브랜치

  • develop 브랜치에서 개발된 코드들에 이상이 없을 경우
    배포를 위해 main 브랜치로 merge함

 

develop 브랜치

  1. 팀원들이 프로젝트를 fork 한 후 develop 브랜치에서 작업함
  2. 팀원들의 develop 브랜치에서 원본 저장소 develop 브랜치로 PR을 요청하고 merge 시킴

CRA 를 이용해 리액트 프로젝트 초기화

npx create-reat-app [프로젝트 이름]

제일 쉬움


React Router Dom 설치

npm install react-router-dom

 

index.js

createBrowserRouter 로 라우터 생성 + RouterProvider 에 해당 라우터 넣어주기

초기 테스트를 위해 보여줄 페이지 컴포넌트들을 몇 개 넣어놓았음

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

...

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
    errorElement: <NotFound />,
    children: [
      { index: true, path: '/', element: <Home /> },
      {
        path: '/login',
        element: <Login />,
      },
      {
        path: '/signup',
        element: <Signup />,
      },
      ...
    ],
  },
]);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <RouterProvider router={router} />
);

Redux Toolkit 설치

npm install react-redux @reduxjs/toolkit

 

store.js

import { configureStore } from '@reduxjs/toolkit';
import loading from './loadingSlice.js';

export default configureStore({
  reducer: {
    loading: loading.reducer,
  },
});

 

index.js

Provider 추가 + store 넣어주기

import { Provider } from 'react-redux';
import store from './store/store';

const router = createBrowserRouter([ ... ]);

...

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
);

 Axios 설치

npm install axios

 

api는 그냥 호출하면 되는 줄 알았는데..........ㅎ

아니었음

 

api 설명을 보니 각 api 마다

요청 시 header 에 들어가야 하는 값도 다르고

호출해야 할 http 메서드도 다르고

데이터 포함 여부도 달랐음.......ㅠㅠㅠ

 

일단 우리 팀이 사용할 api를 호출할 때에는

세 가지 헤더가 필요했음

 

1. 기본 헤더

2. token 이 필요한 헤더

3. masterKey 가 필요한 헤더

 

그래서 axios 인스턴스를 저 세가지로 나누어

미리 만들어놓고 가져다 쓰기로 함

 

utils.js

코드의 재사용을 위해

미리 필요한 인스턴스를 만들어 놓음 (feat. 구글 짱!!!)

import { getItem } from '@/utils/storage';
import axios from 'axios';

const MASTER_KEY = { masterKey: 'true' };

const getHeader = (option) => {
  const header = {
    'content-type': 'application/json',
    apikey: process.env.REACT_APP_API_KEY,
    username: process.env.REACT_APP_USER_NAME,
  };
  if (option) Object.assign(header, option);
  return header;
};

// 기본 인스턴스
const axiosApi = (url, options) => {
  const instance = axios.create({ baseURL: url, headers: getHeader(), ...options });
  return instance;
};

// token 추가 인스턴스
const axiosAuthApi = (url, options) => {
  const token = getItem('token');
  const accessToken = token ? { Authorization: `Bearer ${token}` } : '';
  const instance = axios.create({
    baseURL: url,
    headers: getHeader(accessToken),
    ...options,
  });
  return instance;
};

// master key 추가 인스턴스
const axiosAdminApi = (url, options) => {
  const instance = axios.create({
    baseURL: url,
    headers: getHeader(MASTER_KEY),
    ...options,
  });
  return instance;
};

export const defaultInstance = axiosApi(process.env.REACT_APP_BASE_URL);
export const authInstance = axiosAuthApi(process.env.REACT_APP_BASE_URL);
export const adminInstance = axiosAdminApi(process.env.REACT_APP_BASE_URL);

 

requests.js 

http 메서드 별로 함수를 만들어

공통으로 사용할 수 있게 함 (post, get, put, delete)

인자로 경로, axios 인스턴스, 데이터를 받음

 

아래에는 해당 함수를 호출하는 또 다른 함수를 정의함

외부에서 이 함수를 호출만 하면 바로 사용 가능 !!!

import PATH from '@/constants/path.js';
import { defaultInstance, authInstance, adminInstance } from './util.js';

const requestPost = async (path, instance, data) => {
  return await instance.post(path, JSON.stringify(data)).then((res) => {
    return res.data;
  });
};

const requestGet = async (path, instance) => {
  return await instance.get(path).then((res) => {
    return res.data;
  });
};

const requestPut = async (path, instance, data) => {
  return await instance.put(path, JSON.stringify(data)).then((res) => {
    return res.data;
  });
};

const requestDelete = async (path, instance, data) => {
  if (data) {
    return await instance.delete(path, { data }).then((res) => {
      return res.data;
    });
  } else {
    return await instance.delete(path).then((res) => {
      return res.data;
    });
  }
};

export const searchProduct = (data) => {
  return requestPost(PATH.SEARCH, defaultInstance, data);
};

export const getListBank = () => {
  return requestGet(PATH.BANKS, authInstance);
};

export const deleteProduct = (id) => {
  return requestDelete(`${PATH.PRODUCT}/${id}`, adminInstance);
};

...

경로 상수 파일 생성

path.js

api 호출 시 필요한 패스들을 미리 정의함

혹시 모를 경로의 오타를 줄일 수 있고

한 곳에서 관리할 수 있음

const PATH = {
  SIGNUP: '/auth/signup',
  LOGIN: '/auth/login',
  AUTH_ME: '/auth/me',
  AUTH_USER: '/auth/user',
  BANKS: '/account/banks',
  ACCOUNT: '/account',
  PRODUCT: '/products',
  SEARCH: '/products/search',
  BUY: '/products/buy',
  BUY_CANCEL: '/products/cancel',
  BUY_OK: '/products/ok',
  TRANSACTION: '/products/transactions',
  TRANSACTION_ALL: '/products/transactions/all',
  TRANSACTION_DETAILS: '/products/transactions/details',
  TRANSACTION_DETAIL: '/products/transactions/detail',
};

export default PATH;

env 파일 생성

.env.local / .env.development

api 키 값 등 노출되면 안되는 정보들 작성

REACT_APP_BASE_URL = 베이스유알엘
REACT_APP_API_KEY = 에이피아이키
REACT_APP_USER_NAME = 유저네임
REACT_APP_ADMIN_EMAIL = 관리자이메일

Prettier 설치 및 셋팅

개발 시에만 사용할 것이기 때문에

개발 의존성 설치

npm install -D prettier

 

prettier.config.js

루트 경로에 파일 생성 후 아래 내용 작성

module.exports = {
  trailingComma: 'es5',
  tabWidth: 2,
  semi: true,
  singleQuote: true,
  printWidth: 100,
};

공통 scss 변수 파일 생성

_variables.scss

공통으로 사용할 scss 변수 작성

$color-black: #404040;
$color-white: #fff;
$color-darkgray: #5a5a5a;
$color-gray: #dadce0;
$color-lightgray: #eeeeee;
$color-purple: rgb(95, 0, 128);
$box-shadow: rgba(0, 0, 0, 0.07) 0px 3px 4px 0px;
$box-shadow-deep: 0 1px 6px 0 rgba(32, 33, 36, 0.28);
...

여기서 문제가 발생...!!!

 

공통 변수를 사용하는 scss 파일마다

이 공통 scss 파일을 include 해줘야함

 

scss 파일에 일일이 _variables.scss 파일을 include를 해주지 않아도

컴파일 시 자동으로 불러오도록 설정하고 싶었음

 

그러려면 웹팩 config 파일 수정이 필요한데...

CRA 프로젝트에서 웹팩 config 파일을 보려면 eject 을 시켜야함.......ㅎ (다시 못돌림)

 

그래서 찾아본 결과 !!!

craco 를 사용하기로 함


craco 설치 및 셋팅

craco : CRA 프로젝트에서 설정을 변경할 수 있게 해주는 라이브러리

 

npm install @craco/craco

npm install -D craco-sass-resources-loader craco-alias
  1. craco 설치
  2. craco-sass-resources-loader + craco alias 개발 의존성 설치

 

package.json

기존의 react-scripts를 craco로 변경

"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
  "eject": "craco eject"
},

 

jsconfig.json

루트 경로에 파일 생성 후 아래 내용 작성

{
  "extends": "./jsconfig.paths.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": [
    "src",
    "jsconfig.paths.json"
  ]
}

 

craco.config.js

루트 경로에 파일 생성 후 아래 내용 작성

const CracoAlias = require('craco-alias');

module.exports = {
  style: {
    sass: {
      loaderOptions: {
        additionalData: `
          @import "@/_variables.scss";
        `,
      },
    },
  },
  plugins: [
    {
      plugin: CracoAlias,
      options: {
        source: 'jsconfig',
        jsConfigPath: 'jsconfig.paths.json',
      },
    },
  ],
};

문서화 초기 셋팅

프로젝트 팀 노션에 아래의 항목들을 미리 작성해 놓고 공유함

  • 레포지토리 주소
  • 초기 셋팅에 관한 README
  • 개발 파트
  • 페이지/컴포넌트 명세서
  • 테스트 계정
  • 커밋 규칙

 

페이지/컴포넌트 명세서

파일명, 구분, 설명, 사용주체, 기능, 담당자, 경로, 상태를 표로 만들어 관리함

 


이렇게 프로젝트 초기 셋팅은 끝!!

일단 라우터, api 호출 테스트를 위해서

내브바(로그인), 마이페이지 정도 틀만 만들어 놓았음

 

나머지는 팀원 분들이 야무지게 채워주셨다 🥹❤️