[Spring] 스프링 프레임워크의 특징 (POJO)

kai6666·2022년 6월 18일
0

TIL. Spring

목록 보기
1/11

🍃 스프링 프레임워크(Spring Framework)

스프링 프레임워크(줄여서 스프링)은 오픈 소스 기반의 기술로, Java 기반의 웹 애플리케이션을 만드는 개발자가 되기 위해서 반드시 익혀야 하는 기술이다.

먼저 프레임워크란, 개발자가 애플리케이션의 핵심 로직을 개발하는 것에 집중할 수 있도록 기본적인 프로그래밍을 위한 틀이나 구조를 제공하는 것이다. 프레임워크를 사용하면 개발자가 애플리케이션의 사소한 부분까지 일일이 개발할 필요없이 프레임워크에서 제공하는 라이브러리를 활용해 다양한 기능을 구현할 수 있다.

👉 Framework vs. Library

  • 프레임워크:
    • 핵심 로직으로 채울 수 있게 기반이 되어주는 애플리케이션의 뼈대
    • 프레임워크가 주도권을 가짐 (프레임워크가 코드를 사용해서 애플리케이션의 흐름 조성)
  • 라이브러리:
    • 구체적이고, 잘 정의된 기능/작업
    • 발자가 호출해서 사용하기 때문에, 애플리케이션의 주도권이 개발자에게 있음

(주도권의 내용은 아래 특징에서 IoC로 자세히 알아볼 예정)

👉 스프링 프레임워크를 배워야 하는 이유

먼저 기업에서 애플리케이션 개발에 있어 Framework를 선택할 때,

  • 개발의 생산성 향상
  • 유지 보수 용이

이 두 가지 사항을 주요하게 고려한다(고 한다). 스프링 프레임워크는 객체 지향 설계 원칙에 잘 맞는 재사용과 확장이 가능한 애플리케이션 개발을 할 수 있게 해준다.

스프링이 도입되기 전에는,

  • JSP를 이용한 애플리케이션
    • View 페이지와 서버쪽 코드가 섞여있는 형태의 개발 방식
    • 클라이언트와 서버측의 코드가 섞여 있어 가독성이 떨어지고, 유지 보수가 극악인 방식이라 JSP 방식으로 개발할 때는 프론트/백을 나누지 않고 개발하는 개발자들이 많았다(고 한다..)

  • 서블릿(Servlet)을 이용한 애플리케이션
    • Servlet: 클라이언트 웹 요청 처리에 특화된 Java클래스의 일종
      (Spring에서 웹 요청을 처리할 때도 내부적으로는 Servlet 사용)
    • 서블릿으로 애플리케이션을 개발한다는 건, Servlet을 위한 Java 코드가 클라이언트 측 코드에서 분리돼 별도의 Java 클래스로 관리된다는 의미다.
    • 클라이언트-서버의 역할이 어느정도 분리된 방식인데, 여전히 코드가 매우 길다는 단점이 있다.

  • Spring MVC를 이용한 애플리케이션
    • 서블릿 클래스의 코드들을 개선시킨 버전이다.
    • 개발자가 클라이언트 요청에 담긴 데이터를 직접 꺼내오거나, 캐릭터셋을 지정해주지 않아도 Spring이 알아서 처리해주는 방식.
    • 여전히 Spring 기반의 애플리케이션의 구조를 잡는 설정 작업이 불편.
    • xml 파일로 설정을 직접 잡아줘야 해서 애플리케이션 만드는 것만큼이나 설정 잡는데 리소스가 들어가서 이 당시 Spring 진입 장벽이 높았다.

  • Spring Boot을 이용한 애플리케이션
    • Spring MVC에서 겪어야 했던 설정의 복잡함을 개선시킨 버전이다.
    • 설정 작업을 Spring이 대신 처리해줘 개발자는 애플리케이션의 핵심 비즈니스 로직에만 집중할 수 있다.

📃 스프링 프레임워크의 특징

스프링 삼각형

🔺 POJO(Plain Old Java Object)

  • POJO = 평범한 Java 객체

  • POJO 프로그래밍

    • 규칙

      • Java나 Java의 스펙에 정의된 것 외의 다른 기술, 규약에 얽매이지 않아야 한다.

        (특정 기술을 상속해서 코드를 작성하면, 이후 애플리케이션의 요구사항이 변하거나 다른 기술로 변경하고자 할 때 코드를 일일이 수정/제거해야 한다. 또한 Java는 다중상속을 지원하지 않아 extends 키워드로 한 번 상속을 하게되면 상위 클래스를 받아서 하위 클래스를 확장하는 객체지향 설계 기법을 적용하기 어려워진다.)

      • 특정 환경에 종속적이지 않아야 한다.

        (시스템 요구 사항이 변경될 경우 코드에서 특정 환경에 종속하는 코드를, 심하면 애플리케이션 전부를 뜯어고쳐야 한다.)

    • POJO 프로그래밍을 해야 하는 이유

      • 특정 환경이나 기술에 종속적이지 않음 = 재사용, 확장 가능한 유연한 코드 + 테스트 단순
      • 저수준 레벨의 기술과 환경 종속적인 코드가 없음 = 코드가 깔끔 = 디버깅 쉬움
      • (중요) 객체지향적인 설계를 제한없이 적용할 수 있다.
  • 스프링은 POJO 프로그래밍을 지향하는 프레임워크다. 이를 위해 세 가지 기술을 지원하는데, 그것들은 바로 스프링 삼각형에서 POJO를 감싸고 있는 IoC/DI, AOP, PSA이다.

1️⃣ IoC/DI(Inversion of Control/Dependency Injection)

IoC(Inversion of Control)

우리말로는 '제어의 역전'이라고 부른다.

  • 일반적인 애플리케이션 제어 흐름
    public class Example {
    	public static void main(String[] args) {
     	System.out.println("IoC");
         }
      }
    일반적으로 Java 콘솔 애플리케이션을 실행하려면 main() 메서드가 있어야 한다. 위 코드를 실행시키면 main() 메서드가 호출되고, System 클래스 -> out 변수 -> println() 순으로 호출이 된다. 이렇게 개발자가 작성한 코드를 순차적으로 실행하는 게 일반적인 제어 흐름이다.

Java 콘솔 애플리케이션이 아닌, 웹 상에서 돌아가는 Java 웹 애플리케이션의 경우에는 main() 메서드가 없다. 클라이언트가 외부에서 접속하는데 애플리케이션 이용중 main() 메서드가 종료되면 안 되기 때문이다.

때문에 서블릿 컨테이너라는 거대한 상자에 서블릿 사양에 맞게 작성된 서블릿 클래스만 존재한다. 클라이언트가 요청을 하면 서블릿 컨테이너 내의 컨테이너 로직(service() 메서드)이 서블릿을 실행시켜준다.

이 경우 서블릿 컨테이너가 서블릿을 제어하기 때문에 애플리케이션의 주도권이 서블릿 컨테이너에 있고, 서블릿과 웹 애플리케이션 간의 IoC(제어의 역,전) 개념이 적용되었다고 한다. 스프링에 적용된 IoC 개념은 아래에서 살펴볼 DI이다.

DI(Dependency Injection)

우리말로는 '의존성 주입'이라고 부른다.

IoC는 서버 컨테이너 기술, 디자인 패턴, 객체 지향 설계 등에 적용되는 일반적인 개념이다. 반면 DI는 제어의 역전을 좀 더 체화 시킨 것으로 볼 수 있다.

  • 의존성
    클래스 A가 클래스 B의 기능을 사용할 때, 클래스 A는 클래스 B에 의존한다.
    클래스 A가 클래스 B의 객체를 생성해서 참조할 때, 클래스 A는 클래스 B에 의존한다.
    이와 같이 하나의 클래스가 다른 클래스의 객체, 기능 등을 사용할 때 의존관계가 성립된다고 한다.

  • 의존성 주입 (!=의존 관계 성립)
    생성자를 통해서 어떤 클래스의 객체를 전달 받는 것. 생성자의 파라미터로 객체를 전달하는 것을 외부에서 객체를 주입한다고 말하는 것이다.
    (의존성 주입의 방법은 다양하지만 가장 많이 사용되는 방법이 생성자를 이용한 DI이다.)

코드로 예시를 들면 아래와 같다.

// 의존 관계 성립의 예
public class Calculator {
	public static void main(String[] args) {
    	Operation operation = new Operation(); // Operation 클래스에 의존
        List<Op> operationList = operation.getOperationList();
        }
 }
 public class Operation {
 	public List<Op> getOperationList() {
    return null;
    }
}

// 의존성 주입의 예 (생성자 이용)
public class CalculatorClient {
	public static void main(String[] args) {
    	Operation operation = new Operation();
    	Calculator calculator = new Calculator(operation); 
        // CalculatorClient 클래스가 Calculator의 생성자 파라미터로 operation을 
        // 전달하고 있기 때문에 객체를 주입해주는 "외부"가 되어줌
        
	public class Calculator {
	 private Operation operation;
    
     public Calculator(Operation operation)
     //Calculator의 생성자로 Operation의 객체를 전달 받음 = 의존성 주입
       {
      	  this.operation = operation;
	   } 
       public List<Op> getOps() {
    	  return operation.getOperationList();
     }
    
 	 public class Operation {
 	    public List<Op> getOperationList() {
     	return null;
     }
}
  • 느슨한 의존성 주입
    위와 같이 new 키워드로 의존 객체를 생성하고 다른 클래스에 주입했을 때, 클래스 간 결합은 해진다. 이러한 클래스가 수 백가지라고 쳤을 때 수정 요청이 들어오면 클래스마다 수동으로 수정해줘야 해서 매우 비효율적이다.
    그렇기 때문에 클래스 간 강한 결합은 지양하고, 느슨한 의존성 주입을 지향하는 것이 객체지향 프로그래밍에서 바람직하다.
    객체지향적으로, 의존성을 느슨하게 주입해주려면 인터페이스를 사용하면 된다.
public class CalculatorClient {
	public static void main(String[] args) {
    	Operation operation = new OperationStub();
        // new로 OperationStub 클래스의 객체를 생성해서
        // Operation 인터페이스에 할당 (업캐스팅)
        // 업캐스팅을 통한 의존성 주입으로 인해 
        //Calculator와 Operation은 느슨한 결합 관계
        
    	Calculator calculator = new Calculator(operation); 
        
	public class Calculator {
	  private Operation operation;
    
      public Calculator(Operation operation)
      // Operation 인터페이스를 가리키는 파라미터
      {
    	 this.operation = operation;
	  } 
      public List<Op> getOps() {
     	return operation.getOperationList();
      }
      
    public interface Operation {
    	List<Op> getOperationList();
      }
    
    public class OperationStub implements Operation {
    	@Override
 	    public List<Op> getOperationList() {
     	return List.of(
        ...
        );
     }
}

여기서 new 키워드를 최소화하여 의존 관계를 더 느슨하게 해야 하는데, 이 부분은 추후 DI 개별 포스팅으로 살펴보겠다.

2️⃣ AOP(Aspect Oriented Programming)

AOP는 우리말로 관심 지향 프로그래밍이라고 봐도 된다. 여기서 관심은 interest가 아닌 Aspect다.

애플리케이션 개발시 어떤 사항이 비즈니스 로직 달성을 위한 것인지에 따라 개발하는 사항을 크게 두 분류로 나눌 수 있다.

  • 핵심 관심 사항(Core concern)
    = 비즈니스 로직
    = 애플리케이션의 주목적 달성을 위한 핵심 로직
    (위 표에서 Presentation, Business, Data Access 레이어에 해당)

  • 공통 관심 사항(Cross-cutting concern)
    = 애플리케이션 전반에 사용되는 공통 기능
    (위 표에서 로깅, 보안, 트랜잭션에 해당)

AOP는 애플리케이션의 핵심 업무 로직에서 공통 기능 로직을 분리하는 것이다. 분리하는 이유는 다음과 같다.

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

코드를 보다 깔끔하게 쓰고, 재사용성을 높이려는 고민이 반영된 프로그래밍이 결국 객체지향적 프로그래밍이다.


3️⃣ PSA(Portable Service Abstraction)

PSA는 한 마디로 추상화 개념이다.

객체지향의 4가지 개념 중 하나인 추상화를 이미 이전 포스팅을 통해 살펴봤기 때문에 간단히 설명하자면,

추상화를 하면 클라이언트는 요청하는 수만큼 다른 개별 클래스를 보는 게 아니라, 추상 클래스만 일관되게 바라보며 하위 클래스의 기능을 쓸 수 있다.

public interface Student {
    String getHomeWork();
}

class StudentA implements Student {
    public String getHomeWork() {
        return "two pages of essay";
    }
}
class StudentB implements Student {
    public String getHomeWork() {
        return "ten math questions";
    }
}

추상화의 가장 기본적인 예시 코드이지만, 이것을 서비스에 접목해서 생각하면 클라이언트가 바로 StudentA나 StudentB 클래스에 접근하는 것이 아닌, Student라는 인터페이스를 환승장처럼 중간에 끼워두고 그 구현체라면 어디든 접근할 수 있게 해주는 것이다.

필요한 리소스에 바로 접근하는 방식 대신 추상화를 통해 인터페이스를 거쳐가게 하는 이유는 이 방식이 서비스의 기능에 접근하는 방식 자체를 일관되게 유지하기 때문이다. 또한 기술 사용이 유연해진다. 이것이 PSA(일관된 서비스 추상화)다.

기술 사용이 유연하다는 것은, 애플리케이션의 요구 사항이 변경됐을 때 대처를 유연히 할 수 있다는 뜻이다. 모두 직접 연결시켰다면 일일이 변경하는 수고를 치뤄야하나, 접근 방식을 일관되게 통일시켜놓았으니 최소한의 변경만으로 요구 사항을 반영할 수 있다.


profile
성장 아카이브

0개의 댓글