kt cloud 부캠을 통해 자바 스프링 환경 백엔드를 공부하며 구현에 집중하다보니 배경에서 놓친 것이 정말 많았다고 생각된다.
토비의 스프링 책을 읽기에 앞서 강의를 통해 간단히 리마인드 해보려 한다.
1. 오브젝트란 무엇인가?
객체지향이라는 말을 수도 없이 들었지만, 막상 '클래스/오브젝트가 무엇이냐?' 라고 물어본다면 쉽게 대답하지 못했다.
이 강의에서 명쾌히 대답하는 방법을 배운 것 같다.
오브젝트란 실제 프로그램이 실행되어서 움직이는 무언가이다.
코드 레벨에서 클래스라는 청사진을 바탕으로 런타임 환경에서 동작하여 결과를 얻게해주는 것이 오브젝트이다.
그렇다면 인스턴스는? 클래스로부터 만들어지는 것이라고 했는데..
클래스의 인스턴스 == 오브젝트 강의에서는 이렇게 표현했다.
'작은 집을 만드는 설계도(클래스)를 하나 만들었다면, 각각 만들어진 집(실체)가 인스턴스'
보통 클래스로부터 생성된 실체를 인스턴스로 표현했는데,
런타임에서 동작하는 실체라는 관점에서 오브젝트와 인스턴스는 동일한 의미로 사용된다.
2. 의존관계
우리는 코드를 작성하며 수많은 의존관계를 만들어낸다.
코드 레벨 단에서 의존관계를 나타내는데, 이는 클래스 간의 의존관계이다.
Client -> Supplier
Client 클래스에서 Supplier 클래스를 의존한다고 해보자.
이 상황에서 Supplier 클래스의 코드가 변경된다면 Client 코드가 당연히 영향을 받는다.
클래스 레벨의 의존관계는 코드를 보면 알 수 있다.
이러한 의존관계는 코드 레벨, 즉 클래스의 의존관계이다.
앞서 살펴보았듯 클래스와 오브젝트는 분명 구분이 필요하다.
따라서, 오브젝트끼리의 의존관계도 존재한다.
클래스와 런타임에서의 의존관계가 다를 수 있는데 이것은 스프링의 핵심이다.
3. 관심사의 분리
Seperation of Concerns
일상생활에서도 관심사가 다른 주제를 휙휙 바꿔 대화하지 않듯 개발도 마찬가지다.
대화가 일단락되면 다른 관심사에 대해 나누는 것이 일반적이다.
컴퓨터는 이러한 관심사가 상관없지만 개발자끼리, 혹은 미래의 나와 소통해야하기 때문에 매우 중요하다.
관심사란?
강의에서 변경이라는 관점으로 관심사를 설명하고 있다.
변경 시점이 다르다면 다른 관심사인 것이다.
1. 환율이 바뀌거나 유효시간 이 변경된다거나
2. 환율 가져오는 방식인 URL을 자바 HTTP connection말고 다른 방식을 사용한다거나
두 가지 로직의 변경 시점이 다르다.
1번은 기술적이거나 환율을 가져오는 메커니즘에 의해 변경될 것이고,
2번은 외환을 어떻게 원화로 변경하냐에 따른 서비스 로직에 따라 변경될 것이다.
따라서 두 가지 로직을 같이 배치하는 것은 좋지 못한 방법이다.
분리한다면 읽기도 편하고 추후 수정하기도 편하다. 가장 쉬운 방법은 메소드 추출이다.(인텔리제이 단축키도 있다.)
4. 상속을 통한 확장
우리는 재사용이 용이하게 코드를 작성하고 사용하려 노력한다.
만약 나의 서비스 코드를 외부에서 사용하고 싶어한다면, 어떻게 할까?
그들은 특정 부분만 본인들의 코드를 사용하려 하고 나는 빌드된 jar파일만 공유하고 싶은데
그렇다면 나는 그들의 코드에 맞게 고쳐서 빌드, 컴파일하여 매번 공유해주어야한다.
public class PaymentService {
public Payment prepare() throws IOException {
// 환율 가져오기
URL url = new URL("https://open.er-api.com/v6/latest/" + currency);
// ...
// ObjectMapper 활용해 원화 키 체크해서 Payment 리턴
return new Payment();
}
private BigDecimal getExRate() {
// ...
}
}
본래 PaymentService는 main 메서드까지 추가되어 이러한 형태로 작성되었다.
이러한 서비스 코드를 외부에서 요청했다면 어떻게할 수 있을까?

제일 간단하게 추상 메서드를 활용한 상속을 통해 이러한 형태로 바꿔줄 수 있다.
getExRate에 의존하지 않고 서비스 코드를 동작시키게 한다.
어떠한 시나리오에서는 굉장히 유효하지만 VIP는 환율 계산 시 유리한 정책을 적용한다거나 하면 조합이 복잡해진다.
또한 상위 클래스와 하위 클래스의 결합도가 매우 높고, 자바는 단일 상속만 지원하다보니
복잡한 상속 구조를 만들어 여러 조합을 펼치거나 하는 것이 불가능하다.
클래스 이름도 굉장히 복잡해진다..
5. 클래스 분리

앞서 제시한 상속의 한계로 인해 상속에서 의존관계로 변경했다.
public class PaymentService {
// 변경 필요
private final SimpleExRateProvider exRateProvider;
public PaymentService() {
// 변경 필요
this.exRateProvider = new SimpleExRateProvider();
}
public Payment prepare() {
// 환율을 가져오는 정책이나 방식이 변경되어도 외부에서 변경 가능
BigDecimal exRate = exRateProvider.getExRate(currency);
return new Payment();
}
}
클래스를 분리해서 의존관계로 변경하니 코드 수정은 매우 줄어들었지만,
그래도 PaymentService 변수의 타입과 함수명을 바꿔줘야하는 번거로움이 남아있다.
6. 인터페이스 도입

인터페이스를 도입함으로써 메서드 이름을 확정할 수 있고, 수정 범위를 PaymentService 생성자 내부로 좁혔다.
새로운 인터페이스의 구현체가 추가되더라도 내부 수정이 최소화된다.
인터페이스를 이용한 다형성의 좋은 예시이지만 아직 결합도가 높다.
7. 관계설정 책임의 분리

6번의 인터페이스 활용 구조는 사실상 이 그림과 동일하다.
PaymentService의 생성자에서 new 연산자를 통해 실제로 인스턴스를 만들어주기 때문에 클래스 레벨에 의존하고 있다.
'관계설정'은 PaymentService가 어떤 클래스의 오브젝트를 사용할 것인가를 맺어주는 것이다.
이 클래스 다이어그램도 결국 코드 레벨의 의존관계를 나타내고 있고,

런타임 환경에서 오브젝트 다이어그램을 나타내면 이렇게 된다.
런타임에서 어떠한 오브젝트를 사용할 지 이정표는 반드시 필요하지만 PaymentService 생성자에서는 제거가 필요하다.
여기서 관계설정 책임의 분리가 나온다.

이 다이어그램대로 코드가 개선된다면 PaymentService의 생성자는
public PaymentService(ExRateProvider provider) {
this.exRateProvider = provider;
}
이런식으로 변화할 수 있다. 익숙한 코드이고 이정도까지 왔으면 상속을 통한 확장에서 괜찮다 수준까지 왔다.
하지만 실제 실행시킬 main 메서드가 필요해서 Client 클래스를 만들었는데, 아직 Client에서 두 가지 관심사를 갖고 있다.
Client가 자기만의 PaymentService를 이용하여 업무 수행을 해야하는데,
PaymentService가 다른 오브젝트와 어떻게 관계를 맺는 지 알아야하는 책임이 아직 존재한다.
public class Client {
// ...
PaymentService service = new PaymentService(new WebApiExRateProvider());
}
이렇게 아직 사용해야한다.
만약 백엔드 웹 코드였다면 Client는 Controller정도였을 것이다.
8. 오브젝트 팩토리

실행하는 측에서는 PaymentService가 어떤 의존관계를 갖고 있는지 알 필요가 없기에 분리가 필요하다.
따라서 ObjectFactory클래스를 생성해서 어떤 ExRateProvider의 구현체를 사용할 지 책임을 넘긴다.
public class ObjectFactory {
public PaymentService paymentService() {
return new PaymentService(exRateProvider());
}
public ExRateProvider exRateProvider() {
return new WebApiExRateProvider();
}
}
팩토리 메서드를 통해 생성 책임을 한 곳으로 모아 Client는 사용에만 집중할 수 있게 된다.
하나의 오브젝트가 의존하는 다른 오브젝트가 있다면 정확히 그것을 찾아와서 연결하는 역할인 것이다.
해당 강의를 보고 작성한 지식 입니다.
'spring' 카테고리의 다른 글
| [Spring] 토비의 스프링 Vol.1 2장 - 테스트 (2) (0) | 2026.06.18 |
|---|---|
| [Spring] 토비의 스프링 Vol.1 2장 - 테스트 (1) (0) | 2026.06.17 |
| [Spring] 토비의 스프링 Vol.1 1장 - 오브젝트와 의존관계 (2) (0) | 2026.06.11 |
| [Spring] 토비의 스프링 Vol.1 1장 - 오브젝트와 의존관계 (1) (0) | 2026.06.09 |
| [Spring] 토비의 스프링 강의 섹션 3 - 스프링 도입 (0) | 2026.05.04 |