포스트

[Frontend] 테스트 코드의 조건

[Frontend] 테스트 코드의 조건

들어가며

프론트엔드 개발에서 테스트는 버그를 줄이고, 리팩토링을 쉽게 만들어주는 중요한 수단입니다. 그렇다면 어떤 코드가 테스트하기 쉬운 코드일까요? 이 글에서는 테스트 용이성을 높이기 위한 조건들과 함께, 관련 예시 코드도 함께 살펴보겠습니다.

목차

  1. 순수 함수 사용하기
  2. 단일 책임 원칙 지키기
  3. 예측 가능한 코드 작성하기
  4. 테스트를 위한 코드 수정, 괜찮을까?
  5. 결론

1. 순수 함수 사용하기

순수 함수란 다음과 같은 특징을 가진 함수입니다:

  • 동일한 입력에 대해 항상 동일한 출력
  • 외부 상태나 환경에 의존하지 않음
  • 부수 효과(side-effect)가 없음

이런 함수는 독립적으로 테스트가 가능해 매우 이상적인 테스트 대상입니다.

✅ 순수 함수 예시

1
2
3
4
// 입력만 받아 출력만 반환하는 순수 함수
export function add(a: number, b: number): number {
  return a + b;
}

❌ 비순수 함수 예시

1
2
3
4
let counter = 0;
export function increment() {
  counter += 1;
}

위 함수는 외부 상태(counter)에 의존하고 이를 변경하므로 테스트하기 어렵습니다.

2. 단일 책임 원칙 지키기 (SRP)

하나의 함수/모듈은 하나의 책임만 가져야 테스트가 쉽습니다. 여러 기능을 동시에 수행하는 함수는 테스트하기 어렵고, 실패했을 때 원인 파악도 어렵습니다.

❌ 좋지 않은 예시

1
2
3
4
5
6
// 데이터 요청 + 렌더링을 한 함수에서 수행
async function fetchAndRenderUser() {
  const res = await fetch('/api/user');
  const user = await res.json();
  document.getElementById('username')!.textContent = user.name;
}

이 함수는 데이터 요청과 DOM 조작이라는 두 가지 책임을 가지므로 테스트가 까다롭습니다.

✅ 개선된 예시

1
2
3
4
5
6
7
8
9
// 역할 분리
export async function fetchUser() {
  const res = await fetch('/api/user');
  return await res.json();
}

export function renderUserName(name: string) {
  document.getElementById('username')!.textContent = name;
}

이제 각각의 함수는 독립적으로 테스트 가능해집니다.

3. 예측 가능한 코드 작성하기

예측하기 쉬운 코드는 테스트하기도 쉽습니다. 이를 위해 다음을 지켜주세요:

  • 명확한 변수명과 함수명
  • 일관된 로직 흐름
  • 상태 변경이 명확히 드러나는 구조

❌ 예측 어려운 코드

1
2
3
function process(d: any) {
  return modify(update(d));
}

✅ 예측 가능한 코드

1
2
3
4
function updateUserData(user: User): User {
  const updatedUser = { ...user, active: true };
  return updatedUser;
}

함수명과 변수명이 명확하므로 이해하기 쉽고, 테스트할 때 어떤 동작이 일어날지 쉽게 예측할 수 있습니다.

4. 테스트를 위한 코드 수정, 괜찮을까? 🤔

테스트를 위해 다음과 같은 과도한 수정은 지양해야 합니다:

  • private 메서드를 억지로 public으로 변경
  • 테스트만을 위한 코드 삽입

하지만, 테스트하기 어렵다는 것은 곧 코드에 구조적 문제가 있을 가능성도 있다는 뜻입니다. 예를 들어 다음과 같은 상황입니다:

  • 하나의 함수가 너무 많은 역할을 수행
  • 외부 의존성이 강하게 결합되어 있음

이럴 때는 테스트 용이성을 높이기 위해 리팩토링을 고려하는 것이 바람직합니다. 이는 단순히 테스트를 위한 변경이 아니라 전체적인 코드 품질 향상으로 이어질 수 있습니다.

5. 결론

조건설명
✅ 순수 함수 사용입력 → 출력의 흐름이 명확하고 예측 가능
✅ 단일 책임테스트 단위가 작고 명확
✅ 예측 가능읽기 쉽고 이해하기 쉬운 코드
⚠️ 테스트를 위한 무리한 구조 변경은 지양하지만 테스트가 어렵다면 리팩토링을 고려해야 함

테스트하기 쉬운 코드는 결국 유지보수가 쉬운 코드입니다. 테스트 코드를 잘 작성하는 것도 중요하지만, 그보다 테스트하기 좋은 구조를 먼저 만드는 습관이 중요합니다.

참고 자료

  • [Clean Code - Robert C. Martin]
  • [Testing JavaScript - Kent C. Dodds]
  • React Testing Library 공식 문서
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.