백엔드 도구들, FE로 치면 무엇일까요?

첫 PR을 올리기까지 코드보다 도구와 싸운 시간이 길었습니다. 커밋하면 처음 보는 린터가 막아서고, 빌드 파일은 어디를 고쳐야 하는지 모르겠고, 테스트를 돌리려니 Docker를 켜라고 합니다. 하나씩 읽다 보니 포맷 검사나 빌드 설정처럼 익숙한 역할이 보였습니다. 반면 DB를 옮기고 변경 이력을 남기는 도구는 사용법부터 새로 익혀야 했습니다.
백엔드의 도구들은 FE의 무엇에 대응하고, 대응물이 없는 도구는 왜 없을까요?
이 글에서 다루는 내용
시리즈의 마지막 글입니다. 코드 품질(ktlint·detekt), 빌드와 구조(Gradle 멀티모듈, 템플릿 레포, 도메인별 레포 분리), 테스트(Testcontainers)를 익숙한 FE 도구와 비교하고, 저장된 데이터를 다루는 도구도 살펴봅니다. 끝에는 시리즈 전체의 FE ↔ BE 대응표를 한 장으로 모았습니다.
- 코드 품질: ktlint와 detekt
- 빌드와 구조: Gradle, 템플릿, 레포 경계
- 테스트: 진짜 DB를 띄우는 Testcontainers
- 저장된 데이터를 다루는 도구들
- 시리즈 한 장 요약: FE ↔ BE 대응표
- References
코드 품질: ktlint와 detekt
커밋을 막아섰던 린터는 ktlint와 detekt였습니다.
| 도구 | 하는 일 | FE 대응물 |
|---|---|---|
| ktlint | 포맷 검사와 자동 수정 | Prettier |
| detekt | 코드 스멜·복잡도 정적 분석 (긴 메서드, 복잡한 조건식 등) | ESLint |
익숙한 도구와 역할이 비슷해 이해하기 쉬웠습니다. 흥미로웠던 것은 baseline이라는 장치입니다. detekt를 기존 코드베이스에 도입하면 위반이 수백 개씩 나오는데, 그걸 다 고치는 대신 현재 위반 목록을 스냅샷으로 얼려두고 새로운 위반만 막습니다. 레거시 코드 곳곳의 eslint-disable 주석을 파일 하나에 모아둔 것과 같은 개념입니다.
여기서 한 가지 데인 것이 있습니다. baseline 항목은 코드 위치가 아니라 코드 내용을 서명으로 기억합니다. 수정한 부분이 서명에 포함되면 기존 항목과 매칭되지 않아 "새 위반"으로 드러날 수 있습니다. 저는 이를 관련 코드를 고칠 때 남아 있던 문제도 함께 살펴보는 계기로 삼았습니다. 다만 이 빚을 갚는 방법은 구분해야 합니다. 다른 사람이 남긴 위반이 다시 드러난 것이면 baseline을 갱신할 수도 있지만, 내가 새로 만든 위반은 baseline에 얼리는 게 아니라 리팩터링으로 없애는 것이 맞습니다.
빌드와 구조: Gradle, 템플릿, 레포 경계
구조를 만드는 도구들은 이 시리즈에서 이미 여러 번 등장했습니다. 역할만 표로 정리하면 이렇습니다.
| 도구/관습 | 하는 일 | FE 대응물 |
|---|---|---|
Gradle 멀티모듈 (settings.gradle) | 모듈 선언·의존 방향·빌드 오케스트레이션 | pnpm workspace + Turborepo |
| 스켈레톤 템플릿 레포 | 새 레포의 출발점, 모든 레포가 같은 구조를 따름 | create-app 계열 보일러플레이트 |
| 도메인별 레포 분리 | 도메인 = 레포. 담당과 맥락의 분리 | 모노레포 패키지 경계, 마이크로프론트엔드 |
settings.gradle에는 프로젝트의 모듈들이 선언되어 있고, 각 모듈의 빌드 설정에는 의존 관계가 적혀 있습니다. 1편과 2편에서 본 모듈 구분을 여기서 실제로 확인할 수 있습니다. 새 모듈을 추가할 때는 기존 모듈의 선언과 의존 설정을 함께 보면 됩니다.
템플릿 레포는 레포가 많아지면서 필요해진 도구입니다. 도메인마다 레포가 늘어나는 구조에서는 "새 레포를 어떻게 시작하는가"가 반복 문제가 되고, 그 답이 모두가 따르는 출발 템플릿입니다. 모든 레포가 같은 구조라는 것은 처음 온 사람에게 큰 선물이기도 합니다. 하나를 익히면 열을 읽을 수 있습니다.
테스트: 진짜 DB를 띄우는 Testcontainers
테스트를 돌리려니 Docker를 켜라고 했던 이유가 이것입니다. Testcontainers는 테스트가 시작될 때 진짜 MySQL 같은 것을 Docker 컨테이너로 띄우고 끝나면 걷어 갑니다. 인메모리 가짜 DB로는 못 잡는 것들(실제 SQL 방언, 제약 조건, 트랜잭션 동작)을 실물로 검증하는 것입니다.
실제 실행 환경에서 검증한다는 점은 Playwright로 브라우저를 띄우는 테스트와 비슷합니다. 여기서는 브라우저 대신 DB를 실행하므로 로컬에 Docker 환경이 필요합니다.
1편에서 분리한 코어의 업무 규칙은 DB 없이 테스트할 수 있습니다. Testcontainers는 그 테스트에서 확인하지 못한 어댑터의 SQL과 DB 동작을 검증하는 데 씁니다.
저장된 데이터를 다루는 도구들
제가 해온 프론트엔드 작업에서는 자주 다루지 않았던 도구도 있습니다.
- 마이그레이션 도구 (Liquibase 등): 8편에서 본 것처럼 기존 데이터를 유지하면서 스키마 변경을 적용합니다. 브라우저의 IndexedDB에도 버전 변경과 업그레이드 처리가 있지만, 여기서는 여러 서버와 사용자가 공유하는 DB의 변경 순서를 관리합니다.
- JPA / Entity (ORM): 객체와 DB 테이블을 매핑합니다. API 응답을 화면용 데이터로 바꾸는 코드와 비교할 수는 있지만, 실제 저장과 조회까지 담당한다는 차이가 있습니다.
온보딩에서 특히 낯설었던 것은 배포 뒤에도 남아 있는 데이터를 다루는 일이었습니다. 새 코드가 동작하는지에 더해 기존 데이터를 어떻게 읽고 옮길지도 확인해야 했습니다.
시리즈 한 장 요약: FE ↔ BE 대응표
아홉 편에 걸쳐 쌓아온 대응표의 전체판입니다.
| BE 개념 | FE에서 가장 가까운 것 | 대응의 핵심 | 다룬 글 |
|---|---|---|---|
| 도메인 모델 | 프레임워크와 분리한 업무 규칙 함수 | 순수한 중심, 바깥을 모름 | 1편 |
| 어댑터 | 렌더러 (react-dom 등) | 갈아끼울 수 있는 바깥 부품 | 1편 |
| 포트 (인터페이스) | 스토리지 인터페이스 계약 | 계약은 안쪽이 소유한다 | 1편 |
| 인바운드 어댑터 | 이벤트 리스너 | 세상이 나를 부른다 | 2편 |
| 아웃바운드 어댑터 | fetch / API 클라이언트 | 내가 세상을 부른다 | 2편 |
| 의존성 규칙 | 모노레포 import 방향 | 덜 바뀌는 쪽으로만 의존 | 2편 |
| 의존성 역전 (DIP) | props 콜백 | 안쪽이 선언, 바깥이 구현 | 2편 |
| 도메인 서비스 | 순수 도메인 util | 프레임워크를 모르는 업무 규칙 | 3편 |
| 애플리케이션 서비스 | 오케스트레이션 훅 | 조율만, 로직은 위임 | 3편 |
| 인가의 자리 | 라우트 가드 | 모델 밖, 흐름의 관문에서 | 3편 |
| 인터널 API | BFF의 서버 간 조립 | 클라 대신 서버가 합친다 | 4편 |
| 모델 ↔ Response 정합 | 리소스 REST vs 뷰모델 논쟁 | 같은 축의 서버판 | 5편 |
| DTO 레이어링 | 응답 타입·뷰모델·폼 상태 어댑터 | 경계마다 자기 표현 | 5편 |
| 필드 마스크 / permit | 폼의 dirty fields | 의도를 값이 아니라 목록에 | 6편 |
| 컬럼 vs JSON 컬럼 | atom 쪼개기 vs 상태 객체 | 그 단위로 일하는 쪽이 누구인가 | 7편 |
| 마이그레이션 | IndexedDB 버전 업그레이드와 일부 유사 | 살아 있는 데이터를 옮기는 일 | 8편 |
| ktlint / detekt | Prettier / ESLint | baseline은 얼려둔 빚 | 이 글 |
| Gradle 멀티모듈 | pnpm workspace + Turborepo | 빌드가 강제하는 경계 | 이 글 |
| Testcontainers | Playwright 통합 테스트 | 흉내 대신 실물 | 이 글 |
긴 시리즈를 함께 읽어주셔서 감사합니다. 언젠가 반대 방향의 온보딩, 그러니까 백엔드 개발자를 위한 프론트엔드 개념 지도를 쓰는 분이 있다면, 이 표가 거울처럼 쓰일 수 있기를 바랍니다.
References
도구 공식 문서
- ktlint: An anti-bikeshedding Kotlin linter
- detekt: Static code analysis for Kotlin
- Gradle: Structuring Projects with Gradle (multi-project builds)
- Testcontainers
