본문으로 건너뛰기

안동민 개발노트

본문 시작

객체 공유와 구현 교체

회원가입·로그인 서비스가 같은 저장소를 공유해야 하는 이유를 확인하고 구현 교체의 변경 범위를 BoardConfig로 제한합니다.

구성 메서드가 호출될 때마다 새 메모리 저장소를 만들면 회원가입 서비스가 저장한 회원을 로그인 서비스가 찾지 못합니다.

클래스가 같아도 인스턴스가 다르면 내부 Map도 다릅니다.

정책 교체의 영향 범위를 구성 파일 하나로 제한한다

Test 구현에서 PBKDF2 구현으로 바꿀 때 서비스와 테스트 코드는 그대로여야 한다.

  1. 고정 역할

    PasswordHasher 계약

  2. Test config

    FastHasher 선택

  3. Prod config

    BCryptHasher 선택

  4. 영향 범위

    구성 파일 한곳만 변경


실패하는 구성

MemberRepository memberRepository() {
    return new MemoryMemberRepository();
}

MemberRegistrationService memberRegistrationService() {
    return new DefaultMemberRegistrationService(
            memberRepository(), passwordHasher());
}

LoginService loginService() {
    return new LoginService(memberRepository(), passwordHasher());
}

두 서비스가 서로 다른 MemoryMemberRepository를 받습니다.

회원가입은 성공하지만 로그인은 회원을 찾지 못합니다.


한 인스턴스 공유

BoardConfig.java
public final class BoardConfig {
    private final MemberRepository members =
            new MemoryMemberRepository();
    private final PasswordHasher passwordHasher =
            new TestPasswordHasher();

    public MemberRegistrationService memberRegistrationService() {
        return new DefaultMemberRegistrationService(
                members, passwordHasher);
    }

    public LoginService loginService() {
        return new LoginService(members, passwordHasher);
    }
}

공유해야 하는 상태의 소유자를 구성 루트가 명시합니다.

모든 객체를 무조건 하나만 만들라는 뜻은 아닙니다.

요청마다 달라야 하는 입력 객체는 매번 만들고, 애플리케이션 전체에서 같은 저장 상태를 봐야 하는 저장소는 공유합니다.

역할을 반환하는 작은 팩터리가 중복 연결을 막는다

서비스마다 저장소를 새로 만들면 같은 애플리케이션 안에 서로 다른 메모리 상태가 생긴다.

  1. Repository

    공유 상태를 입력

  2. Hasher

    선택한 정책을 입력

  3. Service

    완성된 객체 반환

  4. Consumers

    controller·batch가 재사용


해시 구현 교체

운영 환경에서는 구성 코드 한 곳에서 구현을 바꿉니다.

 private final PasswordHasher passwordHasher =
-        new TestPasswordHasher();
+        new Pbkdf2PasswordHasher(passwordSettings);

DefaultMemberRegistrationService, LoginService, MemberRepository는 바뀌지 않습니다.

테스트용 구성은 계속 빠른 가짜 구현을 사용할 수 있습니다.

OCP, DIP, SRP는 한 변경 시나리오에서 함께 드러난다

정책 추가, 구현 선택, 서비스 책임을 각각 분리하면 변경이 올바른 경로로만 흐른다.

원칙적용 지점검증 질문
OCP새 정책 구현 추가기존 서비스 수정이 없는가
DIP서비스의 역할 의존구체 타입을 import하지 않는가
SRPConfig와 Service 분리변경 이유가 하나인가

공유 여부 판단

대상보통의 수명이유
저장소·서비스애플리케이션 동안 공유상태와 설정을 일관되게 사용
가입 요청 DTO요청마다 새로 생성사용자 입력이 서로 다름
로그인 세션로그인마다 생성사용자별 인증 상태

Spring에서는 이런 수명을 스코프라고 부릅니다.

3장에서 싱글톤, 프로토타입, 요청 스코프를 정의부터 배웁니다.

공유 객체는 구성 루트가 한 번 만든다

회원가입과 로그인은 같은 저장소와 PasswordHasher 인스턴스를 공유한다.

  1. Repository

    instance 한 번 생성

  2. Write service

    같은 참조 주입

  3. Read service

    같은 참조 주입

  4. Identity check

    두 service의 저장소 동일


연습 문제

회원가입과 로그인 서비스가 같은 PasswordHasher를 받는지 기록하는 가짜 구현으로 검증하세요.

서로 다른 해시 구현을 받으면 어떤 로그인 실패가 생기는지도 설명합니다.