돌멩이 하나/에러는 미래의 연봉

유틸리티 함수(비밀번호 유효성 검사) 및 테스트 코드 작성

미래에서 온 개발자 2023. 4. 7. 17:20

회원가입과 로그인 시, 이메일과 비밀번호 유효성 검사를 한 다음 유효하지 않은 문자열을 입력시 사용자에게 경고 메시지를 주고자 했다. 

 

로그인 화면

 

이메일은 @와 . 기호가 포함되어 있는지 여부를 기준으로 정규표현식을 사용하여 검사하는 함수를 작성했고, 비밀번호는 8~20자 사이의 영문자와 숫자를 최소 1개 이상 포함하는 경우 유효한 비밀번호라는 기준을 세웠다. 

 

// It checks for a sequence of characters that starts with one or more non-whitespace characters ([^\s@]+),
// followed by an @ symbol, followed by one or more non-whitespace characters for the domain name ([^\s@]+),
// then a . character, and finally one or more non-whitespace characters for the top-level domain ([^\s@]+).
export const checkEmail = (email: string) => {
  const regexp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regexp.test(email);
};

export const checkPassword = (password: string) => {
  const regexp = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,20}$/;
  return regexp.test(password);
};

 

checkEmail 과 checkPassword 함수가 내 의도대로 동작하는지를 알아보기 위해 jest 로 테스트 코드를 작성해보고자 했다. CRA 프로젝트였기 때문에 jest가 기본으로 설치되어 있었다.

jest는 __test__ 라는 폴더 아래에 있는 js(x), ts(x) 파일 또는 .spec.js(x) 또는 .test.js(x) 파일을 찾아낸다(.ts(x)도 마찬가지이다). 

 

testMatch [array<string>]
(default: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ])

출처: https://jestjs.io/docs/configuration/#testmatch-arraystring

 

그리고 Given-When-Then 패턴에 따라 다음과 같은 첫번째 테스트 코드를 작성했다. 

test('should $1', () => {
  // Given
  const data = $4

  // When
  const result = $3

  // Then
  expect(result).toEqual($2)
})
1. 테스트 케이스의 목적을 작성한다.
2. 최종적으로 확인해야하는 부분, 즉 예상하는 값을 작성한다.
3. 어떠한 flow를 검증할지 작성한다.
4. 3번 검증을 위해 어떤 가정이 필요한지 작성한다.

출처: https://jbee.io/react/testing-2-react-testing/#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BC%80%EC%9D%B4%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

import { describe, test, expect } from '@jest/globals';
import { checkEmail, checkPassword } from '../util/authorization/checkPassword';

  test('@가 없는 이메일 주소는 유효하지 않은 이메일 주소입니다.', () => {
    const email = 'kimcoding.com';
    const result = checkEmail(email);
    expect(result).toBe(false);
  });

 

package.json의 script를 다음과 같이 수정하고 npm test를 돌려봤다. (스크립트를 수정하지 않고 npx jest 명령어를 입력해도 똑같이 동작한다.)

  "scripts": {
    "test": "jest"
  }

 

그러자 곧바로 다음과 같은 에러가 떴다.

에러 메시지:
Jest encountered an unexpected token
SyntaxError: Cannot use import statement outside a module

 

테스트 코드 파일에서 import 구문을 사용하는 부분이 문제였고, typescript를 사용하고 있었기에 안내해준 링크에 들어가 @babel/preset-typescript 를 devDependencies 로 설치하고, 안내해준 대로 아래와 같이 babel.config.js를 작성하자 테스트가 작동했다. 

module.exports = {
  presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript']
};

 

하지만 babel.config.js 에서 다음과 같은 eslint 에러가 떴다. 

에러 메시지:
'module' is not defined. eslint no-undef

 

eslint의 공식 문서를 보니 주석을 통해 환경 지정을 해줄 수 있다는 걸 알게 되었다.

To specify environments with a comment inside of a JavaScript file, use the following format:

/* eslint-env node, mocha */

This enables Node.js and Mocha environments.

출처: https://eslint.org/docs/latest/use/configure/language-options#using-configuration-comments

 

babel.config.js 파일 상단에 다음과 같이 주석 한 줄을 추가하니 eslint 에러가 사라졌다.

/* eslint-env node */
module.exports = {
  presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript']
};

 

이렇게 eslint 에러를 해결하고 첫번째 테스트 작동을 확인하고 난 다음, 최종적으로 이메일과 비밀번호의 유효성 검사를 확인할 수 있는 테스트 코드를 다음과 같이 작성했다.

 

 

 

그리고 이제 다 됐나 싶어 테스트를 돌리자 예상치 못한 난관이 또 등장했다. 마지막 테스트가 통과가 안 된다. 🥹

비밀번호 유효성 검사 시, 특수문자의 여부와 상관없이 동작하는 걸 의도했는데 특수문자가 있으면 checkPassword 함수가 무조건 false 를 리턴하고 있다는 걸 알게 되었다. checkPassword의 정규식 패턴에서 [^\W_] 를 추가하여 특수문자를 제외하도록 했다.

 

// 수정 전
const regexp = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,20}$/;

// 수정 후
const regexp = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9^\W_]{8,20}$/;

 

그리고 다시 테스트를 돌리자 10개의 테스트를 모두 통과했다.

테스트 코드 통과

 

이렇게 간단한 테스트 코드를 하나 작성해 보는데도 우여곡절이 늘 차고 넘친다. 새로운 걸 시도할 때면 항상 에러를 맞고, 그 에러를 해결해 나가는 게 이제 점점 당연한 일처럼 느껴진다. 조금의 과장을 보태서 '단 한 번도' 내 뜻대로 모든 것이 순탄하게 흘러간 적이 없었던 것 같다. 이 과정 자체가 개발의 일부라는 생각이 든다. 

코드 구현과 화면 구성을 다 한 상태에서 테스트 코드를 작성하는 게 TDD 와는 거리가 멀어서 무슨 의미가 있나 싶은 생각도 들었는데, 정규식 패턴 자체에 문제가 있었다는 점을 이렇게 뒤늦게 알았다는 것이 또 하나의 소득이다 🥲

간단한 유틸 함수의 단위 테스트 외에 프론트엔드에서의 테스트 코드는 어때야 하는지 좀 더 깊이 알아보고 싶다. 리팩토링에도 끄떡없는 견고한 코드를 작성하고 싶다. 그전에 먼저.. 이제 코드를 정돈해서 pr을 올릴 시간이다. 

 

 

📚 참고자료

 

Getting Started · Jest

Install Jest using your favorite package manager:

jestjs.io

 

Configuring Jest · Jest

The Jest philosophy is to work great by default, but sometimes you just need more configuration power.

jestjs.io

 

[Testing] 2. 프론트엔드, 어떻게 테스트 할 것인가

앞서 프론트엔드 테스트 코드를 작성하면서 마주할 수 있는 몇 가지에 대해 이야기했다. 이번 편에서는 다시 테스트에 대한 내용으로 돌아가 앞서 다룬 이야기들을 기반으로 프론트엔드 입장에

jbee.io