728x90
프론트엔드 챌린지
실무에서 잘쓰이는 기술스택을 가지고 쿠팡 클론코딩 하기

 

🎥 목차

더보기

프로젝트 미션

사용한 기술스택

깨달은 점

오류 해결방법

아쉬운 점

 

🚀 프로젝트 미션

쿠팡 서비스의 로그인 페이지회원가입 페이지에서 사용되는 컴포넌트들을 만들어볼 것입니다.

 

총 네 개의 컴포넌트를 설계하며, 실무에서처럼 다양한 유즈케이스에 탄력적으로 대응할 수 있는 구조에 대해서 고민해봅니다.

 

만든 컴포넌트들은 Storybook을 사용해 누구나 확인할 수 있도록 배포합니다.

 

만들어볼 컴포넌트

  1. Button
  2. Input
  3. Check
  4. CheckGroup

 

👨‍💻 사용한 기술스택 (+ 라이브러리)

프레임워크 : Next.js 

 

라이브러리

@emotion-styled

react-hook-form

storybook

 

배포

chromatic

 

💡 깨달은 점

react-hook-form

사실 react-hook-form을 처음봤지만 익숙했다.

왜냐하면, 이전 넘블 챌린지에서 같이 프론트엔드단을 맡아 개발을 진행했던 개발자님께서 나한테 회원가입 form을 react-hook-form으로 하시는걸 알려주셨기때문이다.

 

그때 당시에는 새로운 라이브러리를 배우는 시간과 내가 가진시간을 따졌을때 제시간에 해당 기능을 구현하지 못할거 같아서 라이브러리를 사용하지 않고 내가 직접 form을 만들었다.

 

지금에서 든 생각은, 그때 배워덨더라면 아쉬움이 남아있지만 이제라도 배우게되었으니 다행으로 여기고 있다.

 

react-hook-form을 알게되면서 가장 찾아봤던 것은 register와 errors이다.

 

register는 validate를 하는 방법, mode, watch, trigger에 대해서 조금 더 알게되었다.

  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({ mode: "all" });

  const email = register("email", {
    required: {
      value: true,
      message: "이메일을 입력해주세요",
    },
    pattern: {
      value: EMAIL_REGEX,
      message: "정확한 이메일을 입력해주세요",
    },
  });

 

erros는 register에 등록된 message를 화면에 표시하는 방법, register에 사용된 여러개의 validate 에러메시지를 화면에 표시하는 것을 조금 더 알게되었다.

// errors.password && 를 넣지 않으면, 비어있는 p태그가 있어서 빈 공간이 나오기 때문에 꼭 넣자
{errors.password && <p>{errors.password.message}</p>}

// password 값과 errors.password 값이 있으면 모든 에러를 화면에 표시한다.
{(watchPassword || errors.password) && (
        <div>
          <Text isCorrect={!errors.password?.types?.patternCombination}>
            영문/숫자/특수문자 3가지 조합 (8~20자)
          </Text>
          <Text isCorrect={!errors.password?.types?.patternSuccessive}>
            3개 이상 연속되거나 동일한 문자/숫자 제외
          </Text>
          <Text isCorrect={!errors.password?.types?.patternEmail}>
            아이디(이메일) 제외
          </Text>
        </div>
      )}

 

storybook

열심히 공식문서와 한국어로 정리한 블로그를 참고하여, button 컴포넌트를 storybook에 등록했다.

배운 기간이 짧아서 button 컴포넌트밖에 구현하지 못했지만, 한번 접해봤으니 다음번에는 더 잘해야겠다고 생각하게되었다.

 

내가 등록한 button 컴포넌트는 type을 변경할 때마다 버튼의 글자색과 바탕색이 서로 바뀌게 된다.

 


 

😥 오류 해결방법

forwardRef

React를 사용하면서 useRef는 해당 컴포넌트에만 사용했지, 다른 컴포넌트에 props로 넘겨주지는 않았다.

그러다가, useForm을 사용하게되면서 다른 컴포넌트에 register로 생성된 값을 props로 넘겨주니 에러가 발생했다.

 

또한, Link 태그에 a 태그를 넣지 않고 다른 컴포넌트를 넣었을때 발생했던 에러인데 거의 비슷한 에러 핸들링이다.

 

오류 | Link 태그에 a태그 대신, 컴포넌트 입력시 발생하는 오류(feat. Next.js)

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()? ❓상황 Next.js 태그에 태그를 넣는것 대신, 컴포넌트를 넣을때 발생하는..

2mojurmoyang.tistory.com

 

storybook absolute path

나는 atmoic 디자인패턴을 원하기때문에 폴더를 자잘하게 생성했다.

다른 폴더에 있는 함수나 값을 가져올때 '../../../~' 이런식으로 import하는 것은 별로 좋아보이지 않았다.

그래서 tsconfig.json에 절대경로를 설정했다.

 

storybook에 내가 만든 button 컴포넌트를 등록할때, 내가 설정한 절대경로를 찾지 못해 오류가 나오는 것이었다.

그래서 다른 사람들이 해결한 방법을 이식하여, 아래와 같은 코드를 작성하니 해결되었다.

const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");

module.exports = {
  stories: [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)",
    "../constants/*.ts",
  ],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
  ],
  framework: "@storybook/react",
  core: {
    builder: "@storybook/builder-webpack5",
  },
  webpackFinal: async (config) => {
    if (config.resolve.plugins) {
      config.resolve.plugins.push(new TsconfigPathsPlugin());
    } else {
      config.resolve.plugins = [new TsconfigPathsPlugin()];
    }
    return config;
  },
};

 

🐧 아쉬운 점

전체 선택 checkBox 구현 실패.

react-hook-form을 이용하여 checkbox를 구현했다.

 

이후, 인터넷 검색을 통해 전체 checkBox를 선택하는 기능을 구현했다.

 

그러나, 한가지 한계에 봉착했는데 에러가 이상하게 잡아진다는 것이다.

 

react-hook-form은 error를 잡아내어 메시지를 표시할 수 있다.

전체 선택 checkBox를 체크하면, 모든 checkBox가 체크가 되는 것 까지는 이상없었다.

그러나, 모든 checkBox가 체크가 되었음에도, error가 나와 에러 메시지가 나오는 것이다.

 

몇날 몇일을 고민해봤지만, 해결할 수가 없어서 결국 해당 기능은 구현하지 못했다.

 

3회차 회고록 작성할때, 다른 사람이 2회차때 구현한 것을 보면서 복기를 해봐야겠다.

 

story book 컴포넌트의 기준은 무엇일까?

로그인페이지와 회원가입 페이지의 절대적인 비중을 차지하는 컴포넌트는 input 컴포넌트이다.

따라서, input 컴포넌트를 가장 공을 들여서 만들었다.

 

내가 input 컴포넌트를 구현하면서 고민을 가장 많이 했던 것이 useForm의 register를 input 컴포넌트에 props로 무엇을 전달해줄지 고민을 했다.

 

1. register 자체를 props로 넘겨준다.

<FormInput type="email" placeholder="아이디(이메일)" {register} />

 

2. register로 생성한 값을 props로 넘겨준다.

  const email = register("email", {
    required: {
      value: true,
      message: "이메일을 입력해주세요",
    },
    validate: {
      patternEmail: (value) =>
        EMAIL_REGEX.test(value) || "이메일을 올바르게 입력해주세요",
    },
  });
  
  
<FormInput type="email" placeholder="아이디(이메일)" {...email} />

 

처음에는 register를 넘겨주었다가, input 컴포넌트에 label이라는 값을 넘겨주어 label이 email일때, password일때, name 일때 등 조건에 맞게 코드를 작성해야하는 불편함이 있어서 register로 생성한 값을 props로 넘겨주게 되었다.

 

storybook의 장점은 값을 변경하여 원하는 색생을 찾는다던지, 내용을 변경할 수 있는 등 쉽게 테스트가 가능하다는 점이다.

 

그러나, 2번째 방법으로 구현한 input 컴포넌트는 input 컴포넌트를 사용하는 부모 컴포넌트에서 값을 만들어서 전달해주기 때문에, storybook으로 테스트를 해도 값을 변경할 수 없었다.

 

이것도 몇날 몇일을 고민을 해도 괜찮은 방법이 떠오르지 않아서, 2번째 방법으로 구현한 input 컴포넌트를 그대로 storybook에 등록하고, 다른 사람들은 어떻게 구현했는지 봐야겠다고 생각하게 되었다.

 

story book 컴포넌트의 기준은 무엇일까?

넘블 챌린지를 개최한 호스트의 input 컴포넌트의 조건은 왼쪽에 해당 input이 어떤것인지 쉽게 확인 할 수 있는 아이콘을 추가해달라고 하셨다.

 

또한, focus했을때는 파란색이고 오류가 나타나면 빨간색 밑 테두리를 표현해달라고 하셨다.

 

내가 든 생각은, 아이콘을 클릭해도 input 컴포넌트의 테두리에 영향이 가야함으로, label 태그를 이용하여 label태그에 style을 구현했다.

 

문제는 storybook에서 input 컴포넌트를 등록할때였다.

 

호스트님께서는 input 컴포넌트를 등록해달라고 하셨는데, 나의 input 컴포넌트는 style이 적용되지 않고, label 태그에 style이 적용되어 있었다.

 

호스트님의 요구사항을 해결하려면 input 컴포넌트에 style을 새로 적용해야한다.

그렇게 되면, 이중으로 style이 적용되기때문에 input 컴포넌트에 style을 넣는 것은 현명하지 않은 선택이었다.

 

그래서, label 태그의 style과 input 컴포넌트의 style이 중복되지 않게 빨간색 테두리가 나오게 구현했지만 focus하면 파란색 테두리가 나오는 것은 구현하지 못했다.

 

이것도 다른 사람이 구현한 것을 보면서 구현해야겠다고 생각했다.

복사했습니다!