토비의 스프링 3.1 정독기 - 오브젝트와 의존관계

문지은·2021년 9월 13일
0

토비의 스프링

목록 보기
1/6

그 동안 스프링으로 꽤나 다양한 프로젝트를 진행해봤다. 쇼핑몰 웹사이트, 학생/교수 웹사이트부터 앱 서버에 이르기까지... 그리고 현재 근무하고 있는 위메프에서도 스프링 부트를 이용해서 백엔드 개발에 참여하고 있다. 그러다보니 나에게 스프링 부트가 주무기가 되었다. 하지만 스프링 부트 내부 동작에 대한 이해가 부족한 것 같아서 스프링 계의 갓이라 불리는 토비님의 책으로 스프링 부트의 지식을 기초부터 차근차근 쌓아가고자 한다! 오늘이 바로 그 첫번째 날~


🚪 들어가며

스프링의 공통 프로그래밍 모델! 즉 스프링의 핵심에 대해서 알고 시작하자.

  1. IoC/DI : 오브젝트 생명 주기와 의존 관계에 대한 프로그래밍 모델로 스프링의 유연성과 확장성을 높여주는 친구들이다. 이에 맞게 설계된 코드만이 스프링의 가치를 제대로 누릴 수 있다.
  1. 서비스 추상화 : 특정 환경, 서버, 특정 기술에 종속되지 않는 즉 이식성이 높은 애플리케이션을 만들 수 있게 해준다.
  1. AOP : 애플리케이션 코드 전반에 산재하는 부가적인 기능을 따로 떼서 관리할 수 있게 해준다.

더 깔끔한 코드 유지를 위한 친구

위의 세가지와 스프링이 제공하는 다양한 기술 API를 이용해서 프로그래밍을 한다면 당신이 바로 스프링 전문가✌️


그렇다면 스프링이 이렇게까지 대한민국 개발계를 휩쓸 수 있었던 요인은 과연 무엇이 있을까?

  1. 단순함 : 기존의 복잡하고 객체 지향 언어의 본질을 잃어가던 EJB와 달리 객체 지향 언어의 특성을 살렸다. 여기엔 POJO가 큰 역할을 했다.
  1. 유연성 : 스프링은 서드 파티 지원이 많기로도 유명하다. 그만큼 유연성과 확장성이 매우 높다는 소리.

스프링의 핵심은 객체 지향이고 이에 따라서 오브젝트에 가장 큰 관심을 가지게 된다.

DAO를 살펴보며 이를 이해해보자.

프로그램은 끊임 없이 변화하는 존재이다. 그에 따라서 우린 변화가 쉬운, 즉 변경, 발전, 확장이 쉬운 프로그래밍을 해야한다. (이것이 객체 지향의 참 좋은 점이기도 하다.) 우리는 분리와 확장에 집중해야 한다.

1. 중복 코드 분리

중복되는 코드를 따로 함수로 빼서 관리한다. -> 관심사를 분리함과 동시에 중복을 줄여준다. -> 해당 부분에 대한 수정이 일어날 경우 이 함수만 고치면 된다.
이 과정을 리팩토링이라고 부른다.

2. 상속을 통한 확장

한 함수가 쓰이는 대상에 따라 다르게 동작하도록 만들고 싶다. 비슷하지만 다른 이 코드,, 어떻게 중복을 피할까? 방법은 바로 상속에 있다.

해당 함수 abstract로 만들기 -> 해당 클래스를 상속 받아서 override로 각각 재정의

이것을 템플릿 메소드 패턴이라고 부른다.

  • 템플릿 메소드 패턴 : 상속 받아서 필요에 맞게 구현해서 사용하도록 함
  • 팩토리 메소드 패턴 : 서브 클래스가 오브젝트 생성 방법을 결정하도록 함

하지만 이것도 단점이 있다.

바로 상속을 사용했다는 단점이다. 자바는 다중 상속이 불가능하기 때문에 목적이 여러개라도 여러개의 클래스를 상속 받을 수 없다. 또한 상속도 관심사를 완전히 분리했다고 보기 힘들다. 여전히 상속 관계는 너무 가깝다!

그렇다면 더 화끈하게 아예 다른 클래스로 분리한다면 어떻게 될까? 그랬더니 기능에 따라 다형성을 추구할 수가 없어졌다. 이 때 등장한 것이 바로 인터페이스 이다.

위에서 적용한 것을 전문적인 개념을 이용해 설명해보도록 하자.

개방 폐쇄 원칙 (open closed principle)

클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
인터페이스를 사용하면 기능 추가에는 열려있지만 해당 기능 변경에는 닫힌 모습을 볼 수 있다.

높은 응집도와 낮은 결합도

응집도가 높은 코드는 변화가 일어날 때 하나의 클래스나 모듈에서 변하는 부분이 크다.
결합도가 낮은 코드는 관심사가 다른 오브젝트와는 느슨한 결합을 유지해야 한다. 변경할 부분이 생겼을 경우 수정하는 코드가 전파되지 않도록 한다.

전략 패턴

필요에 따라 변화하는 부분은 따로 인터페이스로 뺀 다음에 필요에 맞게 구현한 뒤 선택하는 방식

즉, 전략을 바꿔가며 사용 가능

제어의 역전 / IoC

클래스가 자신의 제어권을 가지는 것이 아니라 팩토리 클래스를 만들어서 대상의 생성 및 선택의 책임이 역전된다. 즉, 필요한 메소드 들을 빈으로 등록해놓은 뒤에 필요에 따라 빈 컨테이너에서 가져다가 쓴다.

싱글톤 패턴

서버에서 여러 개의 클라이언트가 동시 접속 하는 상황에서 각각의 클라이언트마다 오브젝트를 생성하지 않고 하나의 오브젝트를 재사용하여 서버의 과부하를 줄이는 패턴

싱글톤의 대표적인 형태를 코드로 나타내면 다음과 같다.

public class UserDao {
	
    //static으로 오브젝트 선언
	private static UserDao Instance;
    
    //private 생성자
    private UserDao(ConnectionMaker connectionMaker)
    {
    	this.connectionMaker = connectionMaker;
    }
    
    //오브젝트가 null일 경우에만 새로 생성, 아니면 재사용
    public static synhronized UserDao getInstance()
    {
    	if (Instance == null)
        {
        	Instance = new UserDao(~#@!#);
        }
        
        return Instance;
    }
}

하지만 단점이 있다.

  1. private 생성자 -> 상속이 되지 않는다.

  2. 테스트 하기 힘들다. (1번과 맥락 동일)

  3. 오브젝트가 하나만 만들어지는 것을 보장하지 않는다.

  4. 전역 상태의 오브젝트를 사용하기 때문에 동시성 이슈가 일어날 수 있다.

의존관계 주입 / Dependency Injection

IoC 방식 중 하나. 클래스 간의 상관 관계가 있을 때 이 관계를 느슨하게 하기 위해서 사용한다. 인터페이스를 사용할 수도 있지만 구현한 클래스를 알고 있어야 하기 때문에 충분히 관계가 느슨하지 못했다.

이를 해결하기 위해서 존재하는 것이 바로 의존관계 주입(Dependency Injection).

// DI 적용 전
public class UserDao {

    // 인터페이스 상속 받아서 구현한 클래스를 직접 입력해줘야 함
	connectionMaker = new DConnectionMaker();
}

// DI 적용 후
public class UserDao {
	
    // 의존 관계 맺어줄 오브젝트 선언
	private ConnectionMaker connectionMaker;
    
    // 의존 관계 주입
    public UserDao(ConnectionMaker connectionMaker)
    {
    	this.connectionMaker = connectionMaker;
    }
}
profile
백엔드 개발자입니다.

0개의 댓글