본문 바로가기
public void static main/Book

[토비의스프링] 7장-스프링 핵심 기술의 응용

by 햄리뮤 2025. 2. 28.
반응형

 

 

🌟 DI와 스프링으로 더 나은 코드 설계하기

핵심 개념 한눈에 보기

  • DI의 힘: 객체 연결을 유연하게, 코드 설계를 깔끔하게.
  • 스프링의 도구: 반복 작업 줄이고 생산성 높임.
  • 목표: 변경에 강하고 유지보수 쉬운 코드 만들기.
  • 철학: 객체지향 설계를 실현하는 기반 제공.

🛠️ 설계 단계별 상세 정리

1️⃣ 변경 가능한 데이터 외부화

  • 왜 필요한가?:
    • SQL, 설정값 같은 텍스트 데이터가 코드에 섞이면 수정할 때마다 컴파일 필요 → 비효율적.
  • 어떻게 하나?:
    • 외부 리소스 사용: .sql 파일, .properties, DB 테이블에 저장.
    • 가져오기: 런타임에 동적으로 로드.
  • 구체적 예시:
    • SQL 쿼리를 queries.sql 파일에 저장 → SqlLoader 클래스로 읽기.
    • @Value("${db.query}").properties에서 값 주입.
  • 장점:
    • 코드와 데이터 분리 → 수정 시 코드 건드릴 필요 없음.
    • 배포 없이 설정 변경 가능.

2️⃣ 혼잡한 클래스 정리

  • 문제:
    • 하나의 클래스에 데이터 처리, 비즈니스 로직, UI 로직 등이 섞여 있음.
    • 예: UserManager가 DB 쿼리, 사용자 인증, 로그 기록까지 다 함.
  • 해결 과정:
    1. 인터페이스 정의:
      • UserRepository(데이터), UserService(비즈니스), UserLogger(로깅)로 역할 분리.
    2. 코드 분리: 각 인터페이스에 맞춰 구현 클래스 작성.
    3. 접근 방식: 다른 기능은 인터페이스를 통해 호출.
      • 예: UserServiceUserRepository를 DI로 사용.
  • 검증 방법:
    • 자기참조 빈:
      • 클래스 내부에서 @Autowired로 자신을 주입해 테스트.
      • 예: UserService 내부에서 this.repository.findUser() 호출로 동작 확인.
    • 검증 후 → 별도 클래스로 분리(UserRepositoryImpl 등).
  • 실무 팁:
    • 분리 전 단일 클래스에서 점진적 리팩토링 시작 → 안정성 확보 후 완전 분리.

3️⃣ 자주 쓰이는 의존 객체 관리

  • 아이디어:
    • 반복적으로 사용하는 객체를 디폴트 값으로 설정.
  • 구체적 예시:
    • DataSourceHttpClient처럼 공통 객체를 @Bean으로 미리 정의.
    • @Configuration 클래스에서 기본 설정 제공:
@Bean
public HttpClient defaultHttpClient() {
    return new HttpClient("https://api.example.com");
}
장점:
  • 모든 클래스에서 직접 생성하지 않아도 주입받아 사용.
  • 설정 변경 시 한 곳만 수정하면 됨.

🚀 스프링의 도구 활용 심층 탐구

4️⃣ XML과 객체 매핑

  • 도구: 스프링의 OXM(Object-XML Mapping) 추상화.
  • 상세 설명:
    • XML 데이터를 자바 객체로 변환하거나 반대로 매핑.
    • 지원 기술: JAXB, Castor, JiBX 등.
  • 실무 예시:
    • 외부 API에서 XML 응답(<user><name>John</name></user>)을 받아 User 객체로 변환:
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(User.class);
User user = (User) marshaller.unmarshal(new StreamSource(xmlInput));
  • 장점:
    • 복잡한 파싱 코드 작성 불필요.
    • XML 구조 변경 시 매핑 설정만 수정.

5️⃣ 리소스 사용 최적화

  • 상황:
    • 외부 파일(설정, SQL), 클래스패스 리소스, URL 등을 다룰 때.
  • 도구:
    • 리소스 추상화: Resource 인터페이스로 통일된 접근 제공.
    • 리소스 로더: ResourceLoader(ApplicationContext가 기본 구현).
  • 구체적 예시:
    • 파일 읽기:
@Autowired
ResourceLoader resourceLoader;

Resource resource = resourceLoader.getResource("classpath:queries.sql");
String sql = new String(Files.readAllBytes(resource.getFile().toPath()));
  • 다양한 경로: file:/path, classpath:, http://.
  • 장점:
    • 환경에 따라 리소스 경로만 바꿔도 코드 수정 없음.
    • 테스트 시 가짜 리소스 주입 가능.

6️⃣ 내장형 DB 사용

  • 상황:
    • 개발/테스트용으로 애플리케이션에 DB를 내장.
  • 도구:
    • 내장형 DB 추상화: H2, HSQLDB 지원.
    • 전용 태그: <jdbc:embedded-database>.
  • 구체적 예시:
    • XML 설정
<jdbc:embedded-database id="dataSource" type="H2">
    <jdbc:script location="classpath:schema.sql"/>
</jdbc:embedded-database>
  • 스프링 부트라면 application.propertiesspring.datasource.url=jdbc:h2:mem:testdb 추가.
  • 장점:
    • 외부 DB 없이 빠른 환경 구축.
    • 테스트 후 데이터 자동 롤백.

🌈 DI와 객체지향 설계 심층 분석

7️⃣ DI의 설계적 장점

  • 핵심:
    • DI를 의식하면 객체지향 원칙(단일 책임, 개방-폐쇄 등)이 자연히 적용.
  • 상세 설명:
    • 객체가 스스로 의존 객체를 만들지 않고 외부에서 주입받음 → 느슨한 결합.
    • 예: OrderServicePaymentGateway를 직접 생성 대신 DI로 받음.
  • 실무 이점:
    • 새로운 결제 방식 추가 시 PaymentGatewayImpl만 교체.
    • 테스트 시 MockPaymentGateway 주입으로 검증.

8️⃣ 인터페이스 활용

  • 기본:
    • DI는 인터페이스를 통해 의존성 주입.
  • 상세 설명:
    • 인터페이스 분리 원칙(ISP): 클라이언트가 필요 없는 기능까지 강요받지 않음.
    • 예: UserServiceUserRepository 인터페이스만 의존 → JdbcUserRepositoryJpaUserRepository든 상관없음.
  • 분리 방법:
    1. 새 인터페이스 생성:
      • PaymentGatewayRefundGateway를 별도로 정의.
    2. 인터페이스 상속:
      • BasicUserRepository를 상속받아 AdvancedUserRepository 확장.
  • 실무 팁:
    • 클라이언트 요구사항별로 인터페이스 세분화 → 유연성 증가.

9️⃣ 의존 객체 특화

  • 상황:
    • 특정 기능에 맞춘 의존 객체 필요.
  • 방법:
    • 맴버 클래스:
      • 예: OrderService 내에 PaymentProcessor를 내부 클래스로 정의.
class OrderService {
    private class PaymentProcessor {
        void process() { /* 특화 로직 */ }
    }
}
  • 위임:
    • 중복 로직은 부모 클래스나 별도 객체에 맡김.
    • 예: CommonLogger를 재사용하며 특화 로직만 추가.
  • 장점:
    • 코드 중복 감소.
    • 특화 기능과 공통 기능 분리.

📋 전체 흐름 요약

  1. 데이터 관리: 변경 가능 데이터는 외부로 분리.
  2. 코드 정리: 인터페이스로 역할 분리 → DI로 연결.
  3. 스프링 활용: OXM, 리소스 로더, 내장 DB로 효율성 UP.
  4. DI 설계: 인터페이스 기반으로 유연성 확보.
  5. 최적화: 맴버 클래스와 위임으로 깔끔히 마무리.

💡 핵심 키워드와 한 줄 정리

  • 외부 리소스: "변경은 밖에서 관리해요."
  • 인터페이스: "책임을 나눠주는 설계의 첫걸음."
  • DI: "유연함을 선물하는 연결고리."
  • OXM: "XML을 객체로 바꾸는 마법."
  • 리소스 로더: "어디든 리소스를 찾아줘요."
  • 맴버 클래스: "특화된 기능을 품은 조력자."

 

반응형

댓글