Spring Framework 기본

박윤택·2022년 6월 14일
1

Spring

목록 보기
1/18

🎭 Framework vs Library

Framework란 Frame에서 알 수 있듯이 어떤 대상의 큰 틀이나 외형적인 구조를 의미하는데 프로그래밍 세계에서도 이와 유사한 의미를 가지고 있다. 프로그래밍 상에서의 Framework는 기본적으로 프로그래밍을 하기 위한 어떠한 틀이나 구조를 제공한다.

그렇다면 애플리케이션의 구현을 위해 여러가지 기능을 제공한다는 점에서 Library와 같은 의미니까 같은 것이 아니냐고 생각할 수 있지만 그렇지 않다.

Framework과 Library의 결정적인 차이점은 바로 애플리케이션에 대한 제어권이다.

@SpringBootApplication
@RestController
@RequestMapping(path = "/v1/message")
public class TestApplication {
    @GetMapping
    public String getMessage() {  
        String message = "hello world";
        return StringUtils.upperCase(message); 
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}

getMessage()를 살펴보면 StringtUtils Library를 이용하여 소문자를 대문자로 바꾸는 기능은 upperCase() 메서드를 이용하여 message를 대문자로 바꾸어서 리턴하고 있다. 이는 Library를 사용한 것이다. 개발자가 짠 코드내에 필요한 기능이 있으면 해당 Library를 호출하여 사용하였기 때문이다. 즉, 애플리케이션의 흐름의 주도권이 개발자에게 있는 것이다.

@RestController, @RequestMapping, @GetMappging 어노테이션이 TestApplication class와 getMessage()에 나누어져 붙어져 있다. Spring Framework에서는 각 어노테이션을 이용하여 애플리케이션의 흐름을 만들 수 있다. 개발자는 각 흐름에 맞는 기능들을 코드로 작성해야하기 때문에 애플리케이션의 흐름의 주도권이 개발자가 아닌 Framework에 있다.

  • Library : 애플리케이션 흐름의 주도권 -> 개발자
  • Framework : 애플리케이션 흐름의 주도권 -> Framework

✨ POJO(Plain Old Java Object)

[Spring의 대삼각형]

Java로 생성하는 순수한 객체 즉 평범한 객체를 의미한다.

POJO 프로그래밍

POJO를 이용해서 프로그래밍 코드를 작성하는 것인데 단순히 순수 자바객체만을 이용해서 프로그래밍 코드를 작성한다고 해서 POJO 프로그래밍이라고 볼 수 없다.

크게 두가지 기본적인 규칙이 존재한다.

  • Java나 Java의 스펙에 정의된 것 이외에는 다른 기술이나 규약에 얽매여있지 않아야 한다.
public class MessageForm extends ActionForm{ // (1)
	
	String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
}

ActionForm 클래스는 과거 Struts라는 웹 프레임워크에서 지원하는 클래스이다. 특정 기술을 상속해서 코드를 작성하게 되면 나중에 애플리케이션의 변경이 있을때 Struts 클래스를 명시적으로 사용했던 부분을 전부 변경해야한다. 또한 Java는 다중 상속을 지원하지 않기 때문에 상위 클래스를 상속받아 하위 클래스를 확장하는 객체지향 설계 기법을 적용하기 힘들어 진다. 즉, 외부의 의존성을 두지 않고 순수한 JAVA로 구성이 가능해야 한다.



  • 특정 환경에 종속적이지 않아야 한다.
    순수 Java로 작성한 애플리케이션 코드내에서 Tomcat에서 지원하는 API를 사용한다고 가정했을때 시스템 요구사항이 바뀌어 Zetty를 사용하게 되면 Tomcat API 코드를 모두 수정해야한다.

POJO 프로그래밍이 필요한 이유

  • 특정 환경이나 기술에 종속적이지 않으면 재사용 가능하고, 확장 가능한 유연한 코드를 작성 가능
  • 저수준 레벨의 기술과 환경에 종속적인 코드를 애플리케이션 코드에서 제거 함으로써 코드가 깔끔해짐
  • 코드가 깔끔해지기 때문에 디버깅하기도 상대적으로 쉬움
  • 특정 기술이나 환경에 종속적이지 않기 때문에 테스트 역시 단순해짐
  • 객체지향적인 설계를 제한없이 적용가능

🛒 IoC(Inversion of Control)/DI(Dependency Ingection)

[Spring의 대삼각형]

IoC

IoC(Inversion of Control)는 애플리케이션 흐름의 주도권이 뒤바뀐 것을 말한다. Spring에서는 일반적인 Java 객체를 new 키워드를 이용하여 개발자가 관리하는 것이 아닌 Spring Container에게 모두 맡긴다. 즉, "개발자 -> Framework로 제어의 객체 관리의 권한이 넘어갔음"으로 제어의 역전이라고 한다.


  • 개발자가 작성한 코드를 순차적으로 실행
public class Test {
	public static void main(String[] args) {
    	System.out.println("test");
    }
}

일반적으로 Java 콘솔 애플리케이션을 실행하려면 main() 메서드가 필요하다. main() 메서드가 호출되고 나서 System 클래스를 통해 println()을 호출하게 된다. 그리고 main() 메서드가 종료되면 애플리케이션은 종료된다.

  • 웹 애플리케이션에서의 Java 실행

웹에서 동작하는 애플리케이션의 경우 클라이언트가 계속해서 사용해야하는 서비스이기 때문에 main() 메서드가 종료되지 않아야 할 것이다. 그런데 서블릿 컨테이너는 서블릿 사양에 맞는 서블릿 클래스만 존재하지 별도의 main() 메서드가 존재하지 않는다.

이는 클라이언트의 요청이 들어올때마다 서블릿 컨테이너 내의 컨테이너 로직이 서블릿을 직접 실행시켜주기 때문이다. 이 경우 서블릿 컨테이너가 서블릿을 제어하고 있으므로 애플리케이션의 주도권은 서블릿 컨테이너에 있다. 이는 서블릿과 웹 애플리케이션 간에 IoC의 개념이 적용되어 있는 것이다.

IoC는 의존성을 역전시켜 객채 간에 결합도를 줄이고, 유연한 코드를 작성7할 수 있게 됨에 따라 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 해준다.


DI(Dependency Injection)

DI(Dependency Injection)는 객체를 직접 생성하는게 아니라 외부에서 생성한 후 주입시켜주는 것을 말한다. 이를 통해 모듈 간의 결합도가 낮아져 유연성이 높아진다.

예를 들어 다음과 같은 코드가 있을때 HyundaiCar가 아닌 TeslaCar로 변경할때 직접 CarTest class 내부에서 new 키워드를 이용하여 다른 객체로 변경을 해야한다. 이때 높은 결합도를 가지게 된다. DI는 클래스간의 강한 결합을 느슨한 결합으로 만들어준다. Spring에서는 DI를 Spring이 대신 해준다.

public class CarTest {
	Car car = new HyundaiCar();
    System.out.println(car.getName());
}

DI 유형

1. Setter Injection
의존성을 입력받는 setter 메서드를 만들고 이를 통해 의존성을 주입

@Component
public class TestController {
	private TestService testService;
    public void setTestController(TestService testService) {
    	this.testService = testService;
    }
}

2. Constructor Injection
필요한 의존성을 포함하는 클래스의 생성자를 만들고 이를 통해 의존성 주입

@Component
public class TestController {
	private TestService testService;
    public TestController(TestService testService) {
    	this.testService = testService;
    }
}

3. Field Injection
의존성을 주입할 변수에 @Autowired 어노테이션을 사용

@Component
public class TestController {
	@Autowired
	private TestService testService;
}

👕 AOP(Aspect Oriented Programming)

[Spring의 대삼각형]

애플리케이션에 필요한 기능 중에서 공통적으로 적용되는 공통 기능에 대한 관심과 관련있으며 이를 묶어주는 것을 말한다.

예를 들어 커미 주문 시스템을 만든다고 하자. 기능에는 커피 메뉴 등록, 커피 주문, 커피 주문 변경의 기능을 개발하는데 있어서 각각의 기능에 대해 로깅, 보안, 트랜잭션을 신경써야한다. 이와 같이 공통적으로 사용하는 것에 대하여 공통 관심 사항에 해당된다고 하며 이는 여러 곳에서 사용하므로 공통 기능 로직들을 분리하여 재사용을 한다면 코드를 깔끔하게 작성할 수 있다.

즉, AOP는 애플리케이션의 핵심 업무 로직에서 로깅이나 보안, 트랜잭션 같은 공통 기능 로직들을 분리하는 것이다. 또한 AOP를 이용하여 높은 응집도를 가질 수 있어 SRP 원칙을 지키게 된다.


AOP가 필요한 이유

  • 코드의 간결성 유지
  • 객체 지향 설계 원칙에 맞는 코드 구현
  • 코드의 재사용

📷 PSA(Portable Service Abstraction)

[Spring의 대삼각형]

추상화 된 상위 클래스를 일관되게 바라보며 하위 클래스의 기능을 사용하는 것이 바로 일관된 서비스 추상화(PSA)의 기본 개념이다.


서비스에 적용하는 일관된 서비스 추상화 (PSA)기법

서비스 추상화란 애플리케이션에 사용하는 서비스에 추상화 기법을 사용하는 것을 말한다. 아래의 그림은 클라이언트가 데이터베이스와 연결하기 위해 JdbcConnector를 사용하기 위한 서비스 추상화의 예이다.

즉, JdbcConnector가 애플리케이션에서 이용하는 하나의 서비스가 되는 것이다. JdbcConnector interface를 통해 여러 DB와 연결할 수 있는 Connector들이 간접적으로 연결되어 Connection 객체를 얻을 수 있게 된다.

더 많은 DB를 연결하기 위한 구현체를 작성한다고 하더라도 JdbcConnector의 getConnection() 메서드를 이용하기 때문에 일관된 방식으로 해당 서비스의 기능을 사용할 수 있다. 서비스의 기능을 접근하는 방식 자체를 일관되게 유지하면서 기술 자체를 유연하게 사용할 수 있도록 하는 것을 PSA(일관된 서비스 추상화)라고 한다.


PSA가 필요한 이유

어떤 서비스를 이용하기 위한 접근 방식을 일관된 방식으로 유지함으로써 애플리케이션에서 사용하는 기술이 변경되더라도 최소한의 변경만으로 변경된 요구 사항을 반영하기 위해 필요하다.
즉, 요구 사항 변경에 유연하게 대처할 수 있다.


0개의 댓글