소스 코드를 기록하는 남자

스프링의 삼각형 IoC/DI, AOP, PSA에 대하여

Spring

Java 백앤드 면접을 보러가면 여러 면접장에서 공통적으로 하는 질문들이 있는데, 그 중 빠지지 않고 나온 질문 중 하나가 스프링의 3대 특징에 대해서 이야기하라는 것이였다. 매번 면접 질문에 대한 대답을 하기 위해 정리했었던 부분을 다시 한번 보면서 공부했고, 이를 정리하여 포스팅하기로 마음을 먹었다.

 

면접이 급해서 찾아보았다면 다음과 같이 이해하라.

 

IoC/DI 의존 역전/의존성 주입은  @Autowired나 XML 설정을 통해서 강합 결합을 느슨한 결합으로 변경해주며, 코드를 유연하게 해준다.
AOP 관점 지향 프로그래밍으로서 공통된 로직을 추출하여 메소드의 다양한 시점에 실행할 수 있게 해줄수 있으며, 코드를 줄여주고, 개발자가 공통 로직을 배제하고 핵심 관심사에 집중할 수 있도록 해준다.
PSA Portable Service Abstraction으로 일관성 있는 서비스 추상화이다. 서비스 추상화의 대표적인 예를 JDBC로 들수 있으며, 어떠한 데이터 베이스를 사용하더라도 일관성있는 방식으로 제어할 수 있도록 공통의 인터페이스를 제공하는 것이 서비스 추상화라고 한다.

 

 

스프링 공부를 시작하게 되면 어떠한 개발자라도 반드시 이해하고 가야되는 부분이 스프링의 3대 프로그래밍 모델인 IoC/DI, AOP, PSA다. 따라서 이러한 부분을 모르고 따짜고짜 스프링으로 개발을 하겠다? 이것은 중학교 수학 공식도 모르는 친구가 고교 수학부터 시작하는 것일수도 있다. 물론 그 친구가 천재라면 가능하겠다만, 대다수는 천재가 아니지 않는가.

 

IoC/DI - 제어의 역전/의존성 주입

스프링에서는 제어의 역전을 의존성 주입이라고도 한다. 이를 더 잘 이해하기 위해서는 의존성을 이해해야 한다.

 

프로그래밍에서 의존성?

의존성은 어렵게 생각하지 않아도 단순하게 예를 들자면 다음과 같다.

 

 

운전자는 자동차를 생산한다. = new Car()

자동차는 내부적으로 타이어를 생산한다. = Car 객체 생성자에서 new Tire();

 

 

따라서 new 라는 키워드는 의존성이라 할 수 있다. Car 객체 생성자에서 new 를 실행함으로 Car가 Tire에 의존한다고 볼 수 있다. 이렇게 의존이라는 것은 전체가 부분에 의존하는 것을 표현하며, 좀 더 깊게 들어가서 의존 관계 사이를 집합 관계와 구성 관계로 구분할 수 있으며, 의존 관계를 어떻게 맺냐에 따라서 강합 결합이냐 느슨한 결합이냐를 이야기할 수 있게 된다.

 

집합 관계 부분이 전체와 다른 생성 주기를 가질 수 있다.
구성 관계 부분은 전체와 같은 생명 주기를 갖는다.

 

강한 결합

객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 가지는 구조이다. A 클래스 내부에서 B 라는 객체를 직접 생성하고 있다면, B 객체를 C 객체로 바꾸고 싶은 경우에 A 클래스도 수정해야 하는 방식이기 때문에 강한 결합이다. 위에서도 동일하게 자동차 내부에서 타이어를 생성하는 것은 다른 타이어를 생성하고자 해도 코드를 수정해야 되는 상황이 발생한다.

 

느슨한 결합

객체를 주입 받는다는 것은 외부에서 생성된 객체를 인터페이스를 통해서 넘겨받는 것이다. 이렇게 하면 결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기 때문에 유연한 구조를 가진다.

 

SOLID 원칙에서 O 에 해당하는 Open Closed Principle 을 지키기 위해서 디자인 패턴 중 전략패턴을 사용하게 되는데, 생성자 주입을 사용하게 되면 전략패턴을 사용하게 된다.

 

본격적으로 IoC/DI가 스프링에서 어떠한 역할을 하는지 알아보며 필요성을 느껴보도록 하자.

 

 

객체의 주입?

객체를 주입하는 방법은 생성자를 통한 주입, setter를 통한 주입이 있다. 각 방식에 대해서 살펴보고 어떠한 문제점들이 발생하는지 확인해보자.

 

먼저 객체 내부에서 생성하는 코드부터 확인하자.

 

package 의존성주입;

public interface Dress {
    String getSeason();
}

public class FWSeasonDress implements Dress{
    @Override
    public String getSeason() {
        return "F/W 신상 드레스";
    }
}

public class SSSeasonDress implements Dress{
    @Override
    public String getSeason() {
        return "S/S 신상 드레스";
    }
}

public class Person {
    Dress dress;

    public Person() {
        dress = new FWSeasonDress();
    }

    public String getDress() {
        return "입은 옷은 " + dress.getSeason();
    }
}

 

사람은 옷을 입게 된다. 이 코드에서 확인해보면 사람이 생성될 때 입을 옷이 결정이 된다. 따라서 코드가 유연하지 못하다는 의미이다. 이제 생성자 주입과 수정자 주입을 확인해보자.

 

생성자를 통한 의존성 주입

생성자를 통한 주입 방법은 매우 간단하다. 기존의 생성자 코드를 아래와 같이 변경해주면 된다.

package 의존성주입;

public class Person {
    Dress dress;

    public Person(Dress dress) {
        this.dress = dress;
    }

    public String getDress() {
        return "입은 옷은 " + dress.getSeason();
    }
}

Person의 생성자 부분이 변경되었다. 이렇게 되면 외부에서 Person 객체가 생성될 때 입을 옷을 결정해줄 수 있게 되었다. 

코드가 유연해졌다는 의미이다. 기존의 Person 클래스 코드 변경없이 새로 추가되는 시즌의 옷들을 갈아입혀 줄 수 있게 되었다. 좀 더 실무적인 이야기를 해보자면, 새로운 W/W 시즌의 옷이 나왔을 때 코드의 변경은 Person 객체를 변경해주는 부분과 새롭게 추가되는 W/W 객체만 컴파일해서 배포하면 된다는 의미이다. 

 

Setter (수정자)를 통한 주입

생성자를 통한 의존성 주입을 현실 세계로 예를 들어보면 한번 사람이 태어나면 태어나서 입을수 있는 옷은 고정되어 변경할 방법이 없다는 것이다. 이러한 부분에서 유연성이 떨어지기 때문에 유연성 높은 코드를 작성하고자 한다면 이를 생성자가 아닌 속성을 통해서 의존성 주입을 해야 한다.

 

사실 이러한 부분에서 프로그래밍 진형에서는 생성자를 통한 것이 좋은가? 아니면 속성을 통한 주입이 좋은가에 대한 이야기가 많다. 따라서 사실 무엇이 더 좋다기보단 상황에 맞는 DI를 선택하는 것이 올바르다고 볼 수 있다.

 

아래 글을 참조하면 생성자 주입을 강제하는 것처럼 보일지 모르지만 읽어보면 좋기에 링크를 남긴다.

 

yaboong.github.io/spring/2019/08/29/why-field-injection-is-bad/

 

스프링 - 생성자 주입을 사용해야 하는 이유, 필드인젝션이 좋지 않은 이유

개요 Dependency Injection (의존관계 주입) 이란 Setter Based Injection (수정자를 통한 주입) Constructor based Injection (생성자를 통한 주입) 스프링에서 사용할 수 있는 DI 방법 세가지 생성자 주입을 이용한 순

yaboong.github.io

코드로 변경해보면 아래와 같이 바뀐다.

package 의존성주입;

public class Person {
    Dress dress;

    public void setDress(Dress dress) {
        this.dress = dress;
    }

    public Dress getDress() {
        return dress;
    }

    public String getDressSeason() {
        return "입은 옷은 " + dress.getSeason();
    }
}

public class Driver {
    public static void main(String[] args) {
        Dress dress = new FWSeasonDress();
        Person person = new Person();
        person.setDress(dress);
    }
}

 

생성자가 사라졌고, dress 속성에 대한 접근자와 설정자 메소드가 생겼다. 따라서 언제든지 사람의 옷을 변경 가능하도록 유연한 코드로 변경하게 되었다.

 

이제 스프링에서는 어떻게 의존성을 주입하는 것인가?

 

두가지 방법이 있다.

XML 파일 사용 , @Autowired

 

이 포스팅에서는 깊게 보기보단 간단하게 보겠다. 스프링에는 XML 설정 파일들이 존재한다. 뿐만 아니라 스프링은 Bean을 관리하게 되는데, 내가 코드에서 생성할 Bean을 XML 파일에 등록하여 의존성을 주입할 수 있다. XML 관련 정보는 이 포스팅보다 아래 게시글을 참조하길 바란다.

 

atoz-develop.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-XML-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC

 

[Spring] 스프링 XML 설정 파일 작성 방법 정리

[Spring] 스프링 XML 설정 파일 작성 방법 정리 📄 목차 1. 스프링 XML 설정 파일 포맷 - 기본 포맷 - 애노테이션 설정을 사용하기 위한 포맷 2. 빈(Bean) 설정 예시 - 자동 주입 설정 - autowire 속성 3.

atoz-develop.tistory.com

위 링크에 대한 내용을 이해하고 스프링 의존성 주입이 이루어지는 메커니즘을 이해하게 된다면 스프링을 도입해서 얻고자 하는 목적을 이해할 것이다. 스프링을 도입한다면 코드의 수정없이 XML 파일만 수정하면 프로그램의 실행 결과를 변경할 수 있다는 것이다. 재컴파일이 필요없다는 것은 개발자로서 얻는 강점이 어마어마하다.

 

AOP (Aspect-Oriented Programming) : 관점 지향 프로그래밍

스프링의 DI가 의존성의 주입이라면 AOP는 코드 주입이라고 할 수 있다. 여러 모듈을 개발하다보면 모듈들에서 공통적으로 등장하는 로직이 존재한다. 예를 들어서 입금 출금 이체와 같은 부분에서 보안적인 부분이나 트랜잭션 로그를 남기고자 하는 코드 부분들이 분명히 공통적으로 등장할 것이다.

 

이렇게 공통적으로 등장하는 부분은 횡단 관심사(cross-cutting concern)이라고 한다. 일반적으로 코드는 핵심 관심사 + 횡단 관심사로 구성된다. 따라서 이러한 부분을 @Aspect 어노테이션을 통해서 추출하여 특정 메소드가 호출될 때 특정 시점에 동작하도록 할 수 있는 것이다.

 

이를 통해서 스프링이 얻고자 하는 부분은 어떤 것인가? 공통적으로 등장하는 횡단 관심사를 어느 한 사람이 잘 정의하여 코드를 작성했다면, 다른 개발자들은 이를 재사용할 수 있을 것이다. 이를 통해서 기존의 횡단 관심사를 계속해서 코딩해야 되는 불편함이 사라지고 오직 개발자들은 핵심 관심사에만 집중하여 개발을 할 수 잇게 되는 것이다. 또한 핵심 관심사에만 집중함으로 자연스럽게 SRP을 적용할 수 있게 된다.

 

AOP 개발에 대해서 궁금한 부분이 있다면 아래 링크에서 참조하여 공부하길 바란다.

engkimbs.tistory.com/746

 

[Spring] 스프링 AOP (Spring AOP) 총정리 : 개념, 프록시 기반 AOP, @AOP

| 스프링 AOP ( Aspect Oriented Programming ) AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으..

engkimbs.tistory.com

 

PSA (Portable Service Abstraction) : 일관성 있는 서비스 추상화

PSA는 일관성있는 추상화이다. 서비스 추상화의 대표적인 예로 JDBC를 두는데 이러한 표준 스펙 덕분에 개발자는 오라클을 사용하든, MySQL을 사용하든, MS-SQL을 사용하던 어떠한 데이터베이스를 사용하던 공통된 방식으로 코드를 작성할 수 있다. 데이터베이스 종류에 관계없이 같은 방식으로 제어할 수 있는 이유는 디자인 패턴에서 설명했던 어댑터 패턴을 활용했기 때문이다. 이처럼 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통의 인터페이스로 제어할 수 있게 한 것을 서비스 추상화라고 한다.

 

실질적으로 스프링은 서비스 추상화를 위해 다양한 어댑터를 제공하는데 이러한 부분이 궁금하다면 직접 찾아보는 것을 권한다. 스프링 고수가 되는 그날까지 다들 화이팅이다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Spring boot 기반 Restful api 구현 (3) - Spring boot 프로젝트 설정 문서

Spring/Spring Restful Api

이전까지 Spring boot 기반의 프로젝트 생성에 대하여 알아보았습니다. 

이번에는 프로젝트 설정을 하는법에 대해서 작성해볼게요

 

application.properties

Spring boot 프로젝트는 src/main/resources/application.properties 파일 아래 설정될 내용이 들어갑니다.

아래의 내용을 application.properties에 추가해주세요. 

Server의 포트 설정은 server.port = 원하는 포트넘버 로 가능합니다.

더보기

# root-context and port settings

server.servlet.context-path=/ssafyapi

server.port=8080

JSP( Java Servlet Page ) 사용법

Restful Api 프로젝트를 진행하는 과정에서는 JSP 를 사용하지 않으셔도 됩니다.

혹시라도 Spring MVC 기반을 사용하시는 분들의 jsp 설정에 도움이 될까하여 추가하였습니다.

1. dependency 추가

JSP( Java Servlet Page ) 를 사용하시는 분들은 다음과 같이 설정이 필요합니다.

pom.xml 파일에 아래 dependency를 추가해주세요.

추가한 이후에

프로젝트 파일 오른쪽 클릭 -> Maven -> Update Project 클릭 또는 Alt + F5

더보기

<dependency>

  <groupId>javax.servlet</groupId>

  <artifactId>jstl</artifactId>

</dependency>

<dependency>

  <groupId>org.apache.tomcat.embed</groupId>

  <artifactId>tomcat-embed-jasper</artifactId>

</dependency>

2. jsp path 지정

application.properties에 jsp 경로 지정합니다.

아래는 view의 경로의 prefix를 /WEB-INF/views/ 로 지정해주고 마지막에 .jsp 붙여준다는 의미입니다.

그럼 view 파일의 위치는 WEB-INF/views/ 아래가 되어야겠죠?

더보기

# jsp path

spring.mvc.view.prefix=/WEB-INF/views/

spring.mvc.view.suffix=.jsp

3. 폴더 생성

자 하위 폴더를 생성하여 봅시다.

src/main/webapp으로 가서 하위 폴더 생성 /WEB-INF/views/ 

 

생성했으면 간단하게 jsp 페이지로 데이터를 받아봅시다.

 

views 아래에 hello.jsp 파일을 생성합니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   <!DOCTYPE html>
   <html>
   <head>
   <meta charset="UTF-8">
   <title>Insert title here</title>
   </head>
   <body>
       Hello ${data }<br> 테스트입니다.
   </body>
   </html>

이제 Controller 를 만들어서 데이터를 한번 보내봅시다.

src/main/java 아래 com.anearly.rest.controller 패키지를 생성해줍니다.

생성된 패키지에 helloController.java 파일을 생성합니다.

@Controller
public class HelloController {
	
	@GetMapping("/hello")
	public String hello(HttpServletRequest req) {
		req.setAttribute("data", "테스트");
		return "hello";
	}
}

http://localhost:8080/anearlyapi/hello 로 접속하여

 

 " Hello 테스트 테스트입니다.  " 

 

가 출력되는지 확인합니다.

 

 

MyBatis 설정

본 프로젝트는 MariaDB를 사용할 예정입니다.

https://alpreah.tistory.com/77

 

[Spring] 스프링 마이바티스 마리아디비 연결

#1 Mybatis Spring과 MariaDB연결에 필요한 라이브러리를 다운 받자! 우선, STS와 인터넷에 구글을 켜주세요. 구글에 maven repository를 검색 해 주세요. 만약, 검색 결과가 필자와 다르게 나온다면 여기를 눌..

alpreah.tistory.com

위 블로그를 참고하셔서 MariaDB 설정을 해주세요

Spring boot 기반 Restful api 구현 (2) - Spring boot 프로젝트 생성

Spring/Spring Restful Api

아래와 같이 프로젝트 생성합니다.

 

 

더보기

※ maven 에서 artifact 란 용어는 메이븐 빌드의 결과로 얻을 수 있는 일반적인 jar 나 war 또는 여타의 실행 파일을 의미한다. maven 에서 artifact 는 시스템의 groupId, artifactId, version 에 의해 구분되며 이는 빌드시 필요한 의존성(일반적으로 다른 jar 파일들)을 구분하는데 사용된다

Packaging이 War로 되어있다면 Jar로 변경해줍니다.

Name :: 원하고자 하는 프로젝트 이름을 작성합니다. 저는 anAn-Early-Rest-Api 로 변경합니다.

Group :: com.anearly로 변경하였습니다.

Artifact :: rest로 변경하였습니다.

Package :: com.anearly.rest 로 변경하였습니다.

 

DevTools(자동 서버 로딩), Spring Web, Spring Web Services 3가지 Dependency를 기본적으로 추가하고 시작한다. 이곳에서 Mybatis를 추가 할 수 있으나 따로 pom.xml에서 추가하는것이 오류가 나지 않는다.

 

기존 eclipse에서 진행하던 프로젝트 설정 파일들은 Spring boot로 오면서 xml 기반의 설정파일 다 걷어내게 되었습니다. 대신 application.properties를 이용해서 설정을 추가해줘야 합니다.

 

위와 같이 프로젝트가 생성이 되었으면 실행해봅시다.

 

실행 테스트

"{Project이름}+Application.java" 에서 서버를 실행해 주어야합니다.

서버를 실행한 다음 콘솔창을 확인해봅시다.

아래와 같은 그림에서 에러가 없다면 서버가 문제없이 실행된거에요

boot는 기본적으로 tomcat이 내장되어 있습니다.

src/main/webapp에 hello.html을 추가한 뒤에 아래 페이지로 접속하면 접속이 가능합니다.

간단한 hello.html 파일을 추가해봅시다. 

 

boot의 기본적인 포트 설정은 8080임으로 다음 아래 링크로 접속을 하면 아래와 같은 페이지가 나타납니다.

http://localhost:8080/hello.html

Spring boot 기반 Restful api 구현 (1) - Maven 다운로드

Spring/Spring Restful Api

https://spring.io/tools3/sts/legacy

 

Spring Tool Suite™ 3 (STS 3) Download page

Use one of the links below to download an all-in-one distribution for your platform. Choose either a native installer or simple archive, they contain equivalent functionality

spring.io

1. 위 링크로 가서 STS 3.9.7 RELEASE 를 자신의 운영체제에 맞게 다운로드합니다.

 

2. 압축 해제

 

3. 자신이 사용하기 편한 경로에 폴더자체를 복사해줍니다.

 

더보기

필독

Spring Legacy project를 생성하게 되면 Online에서 필요한 Library를 설치합니다

이 때 C:\Users\UserName\.m2 경로로 다운로드 됩니다.

라이브러리 오류로 인해서 에러가 발생하면 .m2 폴더를 삭제합니다.

삭제하면 다음 실행때 다시 Online에서 라이브러리를 가져오게 됩니다.

 

Spring Project에서는 필요한 라이브러리를 pom.xml 파일에서 dependency 를 추가하여 관리합니다.

따라서 필요한 라이브러리가 있을때 라이브러리를 직접 다운로드 할 필요없이 아래의 링크에서 필요한 라이브러리를 검색한 후에 pom.xml 파일 안 dependencies 사이에 추가해주면 됩니다.

https://mvnrepository.com/