탄탄한 기본기!

[간단 프로젝트] Break-Boredness 본문

개인 공부/React

[간단 프로젝트] Break-Boredness

두영두영 2021. 8. 25. 17:33

Break Boredness (v1.2)

1. 프로젝트 진행 동기

나는 평소에 공부할 때, 공부한 내용들을 진짜 내 것으로 만들었는지 확인하기 위해서 직접 코드로 짜보며 궁금했던 것들을 직접 확인하는 습관이 있다.

 

그래서 최근 리액트를 공부하면서 배운 내용들이 진짜 내 지식이 되었는지, 스스로 코드를 짤 수 있는지 확인하고 싶어서 간단한 프로젝트 하나를 만들어보며 더 많이 공부해보기로 결심했다.

 

그래서 Git 버전 관리, React 개발 환경 (webpack, babel 등) 세팅 및 컴포넌트 상태 관리, 스타일링 방법 등에 중점을 두고 아래와 같이 동작하는 아주 간단한 애플리케이션 하나를 제작해보았다.

 

이번 프로젝트에서는 관리해줄 상태가 매우 적었기 때문에 Redux와 같은 상태 관리 라이브러리는 사용하지 않았고, React의 Context API를 사용해주었다.

 

그리고 디자인도 직접 해야 했기 때문에 (디자인 단계가 생각보다 많은 시간이 필요했다...) figma를 적극적으로 활용했다.

Figma 디자인 시안

이 시안을 토대로 작업을 진행했는데, 생각보다 중간에 디자인 수정이 자잘하게 많이 들어가 최종 결과물은 위와 조금은 상이하다. 

2. 프로젝트 세팅

대부분의 인터넷 강의나 책을 보게 되면 환경 세팅에 대한 강의가 따로 분류되어있지 않는 이상 CRA(Create React App)으로 프로젝트를 초기화하는 것을 시작으로 한다.

 

facebook에서는 CRA를 아래와 같이 소개하고 있다.

You don’t need to install or configure tools like webpack or Babel. They are preconfigured and hidden so that you can focus on the code.

Create a project, and you’re good to go.

 

즉, CRA는 초기 세팅의 수고와 부담을 덜어주고 React Application을 만드는데 필요한 대부분의 세팅들을 미리 해주어 개발자들이 코드에만 집중할 수 있도록 해주는 일종의 매크로인 것이다.

 

CRA로 React 프로젝트를 빠르게 만들어보며 실습하는 것은 매우 편하고 좋은 일이다. 하지만 webpack이나 babel, eslint 등 개발 환경을 스스로 설정하고, 커스터마이징해 원하는 대로 설정할 수 있는 개발자가 되고 싶었기 때문에 설정 또한 직접 해주었다.

 

프로젝트를 세팅하면서 느낀 것은, 프로젝트 개발보다 세팅이 더 어렵다는 것이었고... CRA가 어떻게 세팅되어 있길래 JSX를 사용할 수 있고, css 같은 style도 import 할 수 있는지 등에 대한 궁금증을 해결할 수 있는 방법이 바로 직접 세팅해보며 왜 그런지 아는 것이었다.

 

그래서! 직접 세팅을 진행했는데... webpack 설정의 초기 진입장벽이 너무 높아 Webpack Configuration을 참고하며 여러 가지 프로퍼티들, 옵션들, 플러그인들 등에 대한 내용을 정말 많이 읽어보았다.

 

그리고 마침내 프로젝트의 세팅을 마쳤고, 나중에 기억이 안 날 때를 대비해 기록도 해둘 겸 아래에 이번 프로젝트의 Webpack 설정을 정리해놓았다.

Webpack

const HtmlWebpackPlugin = require('html-webpack-plugin'); // html 플러그인 (템플릿을 활용해 title, 내용 등을 주입해 생성해줌)
const path = require('path'); // 경로 설정 용도
const __ROOT = process.cwd();
const webpack = require('webpack');
const ESLintPlugin = require('eslint-webpack-plugin'); // eslint 설정을 위한 플러그인

require('dotenv').config(); // 'dotenv'를 활성화하지 않을 경우 .env 파일에 설정해놓은 사항들을 읽어들일 수 없음

module.exports = {
  mode: process.env.NODE_ENV, // npm script로 실행할 때에  "--node-env=development 와 같이 옵션으로 주입해줌"
  target: ['web', 'es5'], // 'es5'를 명시해주지 않으면 webpack에서 arrow function과 같은 es6+ 사양을 사용함
  entry: './src',
  devtool: 'source-map', // 디버깅을 위해서 source map 스타일을 설정해주는 것
  output: {
    path: path.resolve(__ROOT, 'dist'),
    filename: '[name].js',
    chunkFilename: '[id].chunk.js',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
    alias: {
      src: path.resolve(__ROOT, 'src'),
      app: path.resolve(__ROOT, 'src/app'),
      components: path.resolve(__ROOT, 'src/components'),
      assets: path.resolve(__ROOT, 'src/assets'),
    },
  },
  devServer: {
    contentBase: path.resolve(__ROOT, 'dist'),
    port: process.env.PORT,
    compress: true,
    hot: true,
    overlay: true,
    stats: 'errors-warnings',
    watchContentBase: true,
    watchOptions: {
      ignored: /node_modules/,
    },
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react', '@babel/preset-env'],
            plugins: [
              'babel-plugin-styled-components',
              '@babel/plugin-transform-runtime',
            ],
          },
        },
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Break Boredness',
      template: path.resolve(__ROOT, 'index.html'),
      filename: 'index.html',
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.ProgressPlugin({
      entries: true,
      profile: false,
    }),
    new ESLintPlugin({
      extensions: ['js', 'jsx'],
      cache: true,
      fix: true,
    }),
    new webpack.DefinePlugin({
      'process.env.END_POINT': JSON.stringify(process.env.END_POINT),
    }),
  ],
};

이때, dotenv.config() 단계를 수행해주지 않아 아래 webpack.DefinePlugin에 원하는 .env 변수들이 설정되지 않았다. 그래서 곧바로 dotenv의 documentation을 읽어보았고, 아래와 같은 사항이 명시되어 있는 것을 확인할 수 있었다.

config will read your .env file, parse the contents, assign it to  process.env, and return an Object with a parsed key containing the loaded content or an error key if it failed.

그리고 css-loader, style-loader 등 스타일링에 대한 설정이 하나도 없는 것을 확인할 수가 있는데, 이는 프로젝트 내부적으로 styled-component를 사용하기로 결정했기 때문이다.

 

css-in-js를 사용하게 된 이유는 먼저, React를 배우며 자바스크립트로 스타일링을 할 수 있다는 사실을 알게 되어 새로 알게 된 지식을 사용해보고 싶었다. 그리고 추가적으로 css-in-css방식과 비교했을 경우 인터렉션이 많은 서비스의 경우 자바스크립트를 매번 로드해 해석해주어야 하기 때문에 css-in-js가 성능적으로 안 좋았지만, 이번 프로젝트에서는 인터렉션이 그렇게 많지 않았으며 테마를 적용함에 있어서 styled-components가 제공해주는 theme Provider로 테마 설정을 쉽게 해 줄 수 있을 것이라고 판단했기 때문이다.

3. 프로젝트 진행 과정

프로젝트를 진행하기 위해서 여러 가지 재미있는 open api 들을 검색하다가 Bored API를 발견했고, 다른 API 들에 비해서 응답 형식이 매우 간단하고, 간단한 실습용 프로젝트라는 주제에도 알맞아 활용했다.

 

그리고 이때까지 배웠던 내용들을 최대한 적절하게 적용하려고 노력했다. 프로젝트의 핵심인 카테고리별 액티비티 랜더링을 위한 Type들은 미리 GlobalVars 디렉터리에 따로 저장해두었고, 상태 관리를 위해서는 Context API와 useReducer훅을 활용했으며 스타일링은 styled-component를 활용했다.

4. 비동기 통신 방법

가장 고민을 많이 했던 부분은 카테고리를 선택해 실제 API를 호출해 결과 페이지를 보여줄 때, 로딩 화면을 보여준 다음 깔끔하게 결과 페이지를 보여주는 방법이었다.

 

getRandomActivity 함수를 통해서 데이터를 받아온 다음 그 결과를 가지고 상태를 업데이트해주어 결과를 보여주는 방식을 선택했는데, 

  1. 결과 페이지 컴포넌트에서 useEffect훅을 활용해 비동기 통신을 한다
  2. 결과 보기 버튼을 클릭했을 때 비동기 통신을 진행해 로딩 후 결과 페이지를 보여준다

이렇게 두 가지 방법을 고민하였다.

 

결과적으로 2번을 선택했는데, 그 이유는 UI 적으로 생각했을 때 까맣게 deem 된 화면에서 로딩 스피너를 보여주다가 결과가 나왔을 때 아래에서 위로 Slide Up 되는 약간 게임(?) 결과 화면 같은 효과를 주고 싶었고, 그렇게 구현하려면 컴포넌트가 mount 되기 전 이미 결과를 모두 가지고 있어야 한다고 판단했기 때문이다.

 

따라서 코드는 아래와 같이 작성했다.

getRandomActivity

export const getRandomActivity = async (type = 'random') => {
  try {
    const { data } = await axios(
      `${process.env.END_POINT}${type !== 'random' ? `?type=${type}` : ''}`
    );

    return data;
  } catch (error) {
    throw new Error('Network error:' + error.message);
  }
};

ClickHandler

const clickHandler = e => {
    dispatch({ type: 'ISLOADING', isLoading: true });
    getRandomActivity(category).then(
      ({ activity, type, participants, price, link }) =>
        dispatch({
          type: 'RESULT',
          result: { activity, type, participants, price, link },
        })
    );
  };

비동기 통신 결과 페이지

약 4일이라는 나름 짧은 기간 동안 디자인, 애플리케이션 flow, 개발까지 모두 마쳤고 Git을 통해 버전도 1.0부터 현재 1.2 버전까지 관리를 해보았다.

 

Git flow전략을 사용해 버전 관리를 해주었는데, 이때까지 git과 github를 원격 저장소쯤으로 밖에 생각하지 않았던 과거를 반성하며... git의 강력한 버전 관리력(?)을 체험할 수 있었고 처음 배워본 React 라이브러리를 직접 사용해보고 Webpack 세팅도 해보며 정말 많이 성장했음을 느낀다.

 

이번 애플리케이션은 너무 간단하며, 일상적으로 필요하지 않은 단순 재미용 프로젝트였는데, 다음엔 실제로 서비스해서 실사용자가 있을 법한 애플리케이션을 만들어보고 싶다. 그리고 사용자들의 피드백을 받아 버전 관리도 계속해보고 싶다.

Comments