리더와 라이터는 아키텍처 규칙이 아니라 선택지다
리더·라이터와 도구 계층을 모방이 아니라 코드 규모, 도메인 거리, 레거시 제약, 팀 규칙에 따라 선택하는 기준을 설명한다.
출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다.
gpt-5.6-sol모델을 사용해 생성·편집했습니다.
Reader와 Writer 클래스는 데이터 접근과 보조 행위 주위에 유용한 경계를 만들 수 있다. 반대로 모든 요청이 이유 없이 거쳐야 하는 계층 하나를 더 만들 수도 있다. 패턴 자체는 어느 결과가 나올지 결정하지 못한다. 그 가치는 현재 코드, 해결하려는 문제, 팀이 지속해서 지킬 수 있는 규칙에 달려 있다.
Reader, Writer와 이를 담는 도구 계층을 선택지로 다뤄야 한다. 그림에 있다는 이유로 도입해서도 안 되고, 서비스가 도저히 바꾸기 어려워진 뒤까지 기다려서도 안 된다. 코드를 관찰하고 압력이 생기는 지점을 찾아 그 문제를 푸는 가장 작은 경계를 만들자.
계층에는 빌려 온 이름이 아니라 역할이 필요하다
‘도구 계층’은 비즈니스 행위를 구현하는 데 쓰이는 구성 요소의 집합을 뜻할 수 있다. Reader, Writer와 비즈니스 흐름 아래의 작고 집중된 협력 객체를 두는 곳이다. 이 이름은 지역적인 용어이지 다른 팀도 채택해야 하는 표준이 아니다. 조직에서 ‘도구’나 ‘컴포넌트’가 역할을 제대로 전달하지 못한다면 다른 이름을 고르고 뜻을 정의하면 된다.
실제로 ‘컴포넌트 계층’이라는 이름을 프레임워크의 컴포넌트 애너테이션이 붙은 코드를 두는 곳으로 오해할 수도 있다. 이름이 그런 잘못된 분류를 반복해서 만든다면 명칭을 바꾸는 편이 낫다. 중요한 것은 특정 용어를 지키는 일이 아니라 비즈니스를 만들기 위해 어떤 도구를 모았는지 팀이 같은 뜻으로 이해하는 것이다.
이름보다 정의가 중요하다. 팀은 이 계층이 무엇을 소유하는지, 의존성이 어느 방향으로 흐르는지, 무엇이 계층 밖에 남아야 하는지 설명할 수 있어야 한다. 그렇지 않으면 같은 아키텍처를 공유하지 않은 채 폴더 구조만 복제하게 된다.
Reader와 Writer도 같은 기준으로 본다. Reader는 읽기 책임을 격리하는 일이 의존성을 명확히 하거나 접근을 통제하기 때문에 존재해야 한다. 모든 리포지토리 호출을 형식적으로 감싸기 위해 있어서는 안 된다. Writer 역시 아무 경계 가치도 없는 전달 메서드를 추가하는 것이 아니라 쓰기 책임을 이해하기 쉽게 해야 한다.
도입 시점은 코드가 받는 압력으로 정하라
추가 도구 계층은 심각한 레거시 코드를 아래에서부터 개선하는 실용적인 방법이 될 수 있다. 비즈니스 서비스에 거대한 함수, 뒤섞인 역할, 강하게 결합된 데이터 접근이 들어 있다면 작은 도구를 추출해 전체 비즈니스 흐름을 먼저 다시 쓰지 않고도 새 경계를 만들 수 있다. 익숙한 레이어 구조 안에 쌓인 책임을 점진적으로 분리하는 전략 하나다.
이때 도구 계층은 기존 구조 위에 이름만 하나 얹는 장식이 아니다. 이미 비대해진 비즈니스 서비스 아래에 집중된 협력 객체를 두어 책임과 의존성을 아래에서부터 정리하는 선택이다. 같은 모양을 새 서비스에 그대로 복사하면 그 배경은 사라지고 간접 호출만 남을 수 있다.
깨끗하고 작은 서비스에는 다른 판단이 필요하다. 로직과 데이터 접근을 여전히 쉽게 이해할 수 있다면 Reader와 Writer를 미리 추가하는 일은 이동 경로와 간접 호출만 늘릴 수 있다. 단순 조회 애플리케이션에는 도구 계층이 필요하지 않을 수 있다. 성격에 따라서는 별도의 비즈니스 계층도 필요 없을 수 있다.
너무 늦게 기다리는 데도 비용이 있다. 비대해진 서비스를 여러 서비스로 나누면 책임을 통제하지 못했을 때 순환 참조가 생길 수 있다. 집중된 하위 계층은 의존성 방향을 잡는 방법 중 하나다. 선택지는 ‘항상 계층을 추가한다’와 ‘절대 추가하지 않는다’가 아니다. 실제 코드의 의존성을 통제할 수 있는 구체적인 방법들 사이에서 골라야 한다.
참조 규칙은 개념 사이의 거리를 반영해야 한다
Reader가 생기면 어느 Reader가 어느 리포지토리를 호출해도 되는지 질문이 따라온다. 도메인과 무관한 하나의 답은 없다. 한 기능이 다른 기능과 관련된 데이터를 필요로 한다고 해보자. 두 개념이 얼마나 가까운지, 같은 경계 안에 있는지, 프로젝트가 어느 정도 결합을 감당할 수 있는지에 따라 결정이 달라진다.
서로 먼 도메인이라면 다른 도메인의 리포지토리에 직접 접근하지 못하게 하는 것이 쓸 만한 기본값이다. 호출자는 상대 도메인의 Reader나 명시적인 다른 도구를 거친다. 데이터 접근 지식을 그 소유자 뒤에 두고, 경계를 넘었다는 사실을 코드에 드러낼 수 있다.
가까운 개념에는 더 느슨한 규칙이 타당할 수 있다. 개념 수가 적은 작은 프로젝트는 도구가 관련 리포지토리를 직접 사용해도 의미 있는 위험이 생기지 않을 수 있다. 복잡성이 트레이드오프를 바꾼다. 작은 코드베이스에서는 무해한 느슨한 참조 구조가 개념과 호출자가 늘어나면 이해하기 어려워진다.
따라서 팀은 큰 의존성 방향을 정하면서도 같은 계층의 참조나 프로젝트별 예외는 열어 둘 수 있다. 시스템을 보존할 만큼 제약하되 합리적인 지역 판단까지 막지 않는 것이 목적이다.
표준에는 이유와 예외 경로가 함께 있어야 한다
서로 다른 숙련도의 개발자가 여러 프로젝트를 오간다면 회사의 기본 구조가 도움이 될 수 있다. 새 동료에게 안정적인 출발점을 주고, 팀마다 모든 구조 결정을 처음부터 다시 찾는 비용을 줄인다. 이런 조직적 필요는 하나의 코드베이스만 볼 때보다 계층을 일찍 도입할 근거가 될 수 있다.
그러나 표준은 설명할 수 있고 조정할 수 있어야 한다. 프로젝트의 README에 선택한 계층과 의도적으로 생략한 계층을 기록하자. 조회 전용 프로젝트는 일반적인 도구 계층을 사용하지 않는다고 밝힐 수 있다. 오염이 심한 레거시 애플리케이션은 경계를 하나 더 둔 이유를 설명할 수 있다. 이런 기록은 기본 규칙을 발전시키면서도 예외가 우연처럼 보이지 않게 한다.
공유 규칙은 프로젝트가 근거를 갖고 덮어쓸 수 있을 만큼 느슨하게 유지하자. 각 계층이 어떤 기여를 하는지 누구도 말할 수 없다면, 모든 서비스에 강제한 4계층 템플릿도 구조 없는 시스템보다 낫지 않다.
오래가는 질문은 “Reader와 Writer를 써야 하는가?”가 아니다. 이 경계가 어떤 실패를 막는지, 언제 코드에 도움이 되는지, 어떤 참조를 통제하는지, 누가 규칙을 유지할지 물어야 한다. 답이 구체적이면 패턴을 사용하자. 그렇지 않다면 설계를 작게 유지하고 코드를 관찰하다 실제 압력이 생길 때 다시 선택하면 된다.