1814 words
9 minutes
테스트하기 좋은 코드, 코드 리뷰의 기준 잡기
2025-06-19

테스트하기 쉬운 코드의 조건들은 무엇인가?

  1. 함수는 순수하게
    • 함수는 순수성을 지키도록 작성한다.
  2. SRP를 지킨다.
    • 하나의 함수, 하나의 모듈은 하나의 책임만 짊어진다.

어제 선언적 프로그래밍에 대해 작성하면서
리액트의 장점은 JSX로 UI단, useState나 다른 걸로 상태단
이렇게 관심사를 분리한 코드를 작성할 수 있다는 것이다.

토스의 use-overlay 같은 라이브러리를 보면 컴포넌트 단에서는
어떤 UI를 보여줄지만 처리하고 상태관리를 훅으로 분리해서 관리한다.

이런 코드 작성법이 곧 테스트하기 좋은 코드와도 연결된다.
코드리뷰할 때도 이런 부분을 염두에 두고 하면
규모가 커져도 사이드 이펙트 우려가 작아지지 않을까 싶다.

커밋메세지 Conventional Commits : 느낌표의 의미#

컨벤셔널 커밋이라는 커밋 메세지 규칙이 있다.
이런 형식으로 작성하는 규칙이다.

<type>[optional scope][!]: <description>
feat(login): add kakao social login
feat: add user signup feature

이 중에 느낌표는 어떤 의미인가?

feat!: remove login endpoint

!는 브레이킹 체인지가 포함되었음을 의미한다.
즉 기존에 있던 기능에 영향을 줄 수 있다는 의미

커밋메세지를 작성할 때도 이런 규칙을 잘 지켜야 할 것이다.

브레이킹 체인지란#

기존 코드에 문제가 발생할 수 있는 변경을 의미한다.
예시

  • 함수 시그니처 변경
  • API 경로 변경
  • 사용자가 의존하던 기능 제거
  • 리턴값 타입 변경
  • 필드 이름 변경 등
    브레이킹 체인지는 주의 깊게 다뤄져야 한다.
    +자동화된 릴리즈 로그 (semantic-release)에서는 주버전 업데이트를 유발한다.

단위 테스트#

단위테스트는 테스트 중에서 가장 작은 단위의 코드를 테스트한다.
함수, 컴포넌트, 훅 등 SRP 원칙에 따라 작성된 이 작은 친구들을 테스트한다.

테스팅에서 생각해야 할 것은

  1. 순수함수인가?
  2. 의존성이 분리되었는가?

의존성 분리는 스프링을 배웠다면 스프링 대표 특징 중에 의존성 주입 개념이 있다.
즉 의존성은 외부에서 주입해줘야 한다. 직접 하지 않는다.

// 안 좋은 예
function saveUser(user) {
  localStorage.setItem('user', JSON.stringify(user));
}

// 좋은 예
function saveUser(user, storage = localStorage) {
  storage.setItem('user', JSON.stringify(user));
}

로컬 스토리지는 외부 저장소이다. 여기에 직접 접근하지 않도록 한다.


프론트엔드 테스트 도구#

도구용도
Jest테스트 러너, 단위 테스트 주로 사용
React Testing LibraryReact 컴포넌트 테스트
VitestVite 기반 프로젝트에서 빠르고 가벼운 테스트 러너
CypressE2E 테스트
PlaywrightE2E + 컴포넌트 테스트 가능

+또 Storybook도 많이 쓴다. 테스팅과는 약간 결이 다를 수도 있는데
UI 컴포넌트를 테스팅 및 확인해보는 용도로 쓰는데 디자이너와의 협업에 유용하다. (사실 안써봄..ㅠ)

아무래도 공통 컴포넌트가 디자인 시스템이 잘 갖추어져 있다면 컴포넌트 단에서 바로 시각적으로 확인하면 전체적인 디자인 일관성에 큰 도움이 될 것 같다.

E2E 테스트#

단위 테스트가 가장 소규모의 단위라고 하면 End-to-End 테스트는 실제 사용자가 사용할 때의 전체 시나리오를 테스트한다.

로그인이나 회원가입 흐름을 검증할 때, 복잡한 폼 입력하고 전송할 때, 페이지 이동+필터+검색 등 뭔가 사용자 시나리오를 기반으로 테스팅한다.

나중에 해봐야겠다.

테스팅을 위해 기존 코드를 바꿔야 할까?#

사실 제일 좋은 건 처음부터 테스팅을 생각하고 코드 짜는 건데 그건 쉽지 않다.
테스팅을 위해 코드를 수정하면 결국 유지보수하기도 쉬워지기 때문에 너어엉무 그렇지 않다면? 수정하자

그렇다면 처음부터 코드를 짤 때 어떤 기준에 따라 짜야 할까

테스팅하기 좋은 코드의 기준#

순수성#

멱등성은 무엇인가?

  • 같은 입력이면 항상 같은 결과값이 나온다.
  • 부작용=사이드 이펙트가 없거나 동일해야 한다.
    멱등성이 보장되는 순수함수가 좋다.

순수함수를 방해하는 2가지 요소#

  1. 제어할 수 없는 값에 의존하는 경우
    사용자 입력이나 개발자가 제어할 수 없는 값에 의존하는 함수는 테스트가 어렵다.
    (환경변수, 전역변수, 외부 라이브러리 내부 상태, 랜덤값 등)

예제#

// ❌ 테스트하기 어려운 예시: 현재 시간에 의존
function getGreeting() {
  const hour = new Date().getHours();
  return hour < 12 ? "Good morning" : "Good afternoon";
}

// ✅ 개선된 예시:
// 시간을 파라미터로 받음
function getGreeting(currentHour) {
  return currentHour < 12 ? "Good morning" : "Good afternoon";
}

// 랜덤 함수를 주입받음
function generateId(randomFn = Math.random) {
  return `user_${randomFn().toString(36).substr(2, 9)}`;
}
  1. 외부에 영향 주는 코드
    외부 API, 데이터 베이스 등에 의존하는 경우
    이 경우 테스트할 때도 외부 환경에 의존해야 한다.

예제#

// ❌ 안좋은 예시 : 여러 책임이 한 함수에 섞임
async function saveUser(userData) {
  // 비즈니스 로직 + 외부 저장이 섞여있음
  const user = {
    id: Date.now(),
    name: userData.name,
    email: userData.email.toLowerCase(),
    createdAt: new Date()
  };
  
  await database.save('users', user);
  console.log('User saved:', user.name);
  return user;
}
// ✅ 좋은 예시
// 순수한 비즈니스 로직
function createUser(userData) {
  return {
    id: Date.now(),
    name: userData.name,
    email: userData.email.toLowerCase(),
    createdAt: new Date()
  };
}

// 외부 효과는 별도 함수로
async function saveUserToDatabase(user, database) {
  await database.save('users', user);
  return user;
}

// 조합 (필요시)
async function registerUser(userData, database) {
  const user = createUser(userData);  // 테스트하기 쉬움
  await saveUserToDatabase(user, database);  // 모킹 필요
  console.log('User saved:', user.name);
  return user;
}

::: note
핵심: 계산(순수함수)과 저장(side effect)을 분리하기! 🎯
:::

또 데이터베이스가 필요하면 테스트가 어렵다.
테스트를 하려면 모킹이든 데이터베이스의 스키마에 맞는 목업 데이터들이 필요해진다.
페이지네이션 같은 것까지 고려하면 100개 이상의 아이템이 필요한 DB를 사용할 수도 있는데 이런 건 테스트에 시간이 소요


외부와의 연동이 필요한 경우 항상 async가 필요한 경우이며 이는 테스트하기가 어렵다.

즉, async 함수를 얼마나 핵심 로직에서 벗어나게 하느냐가 프로젝트 전체의 테스트 용이성을 결정한다.

참고

테스트하기 좋은 코드, 코드 리뷰의 기준 잡기
softourr.github.io/테스트하기-좋은-코드-코드-리뷰의-기준-잡기.md
Author
softourr
Published at
2025-06-19