이해

기술면접을 위한 스프링과 스프링부트 이해

일일일코_장민기 2024. 4. 26. 06:55
728x90

스프링 프레임워크

- 자바 기반 애플리케이션 프레임워크

- 엔터프라이즈급 애플리케이션을 개발하기 위한 다양한 기능 제공

- 전자정부 표준 프레임워크의 기반 기술로 채택되어 공공기관 웹 서비스를 개발할 때도 사용

 

스프링 특징

1. Ioc(제어 역전)(Invertion of control)

- 일반적인 자바: 사용하려는 객체 선언

                     해당 객체의 의존성 생성

                     객체에서 제공하는 기능 사용

                     --> 객체를 생성하고 사용하는 일련의 작업을 개발자가 직접 제어

- 스프링:           사용하려는 객체를 직접 생성 X(객체의 생명주기 관리를 외부에 위임)

                     --> 스프링 컨테이너(IoC 컨테이너)에 객체의 관리를 맡겨 제어권이 넘어감(Ioc)

                     ==> DI(의존성 주입) / AOP(관점 지향 프로그래밍) 등이 가능해짐

                                + 개발자는 비즈니스 로직을 작성하는 것에만 더 집중 가능

라이브러리와 프레임워크의 차이에 대해 공부한 적이 있으면 프레임워크의 제어 역전은 들은 적 있을 것이다.
1) 라이브러리: 클래스의 나열 -> 다른 프로그램에서 사용할 수 있도록 제공되는 도구
2) 프레임워크 -> 개발자가 따라야 하는 가이드, 개발자를 위한 다양한 도구 및 플러그인 제공
==> 이때 가장 큰 차이가 제어의 역전

이것과 연결시키자면
라이브러리 사용 시 제어권은 개발자에게 있으나, 프레임워크 사용 시에는 프레임워크에 종속된다.
이런 프레임워크를 사용하면 개발자가 직접 객체의 생성, 설정, 초기화, 호출, 소멸을 제어/관리할 필요 없다.
대신 외부 컨테이너 / 프레임워크가 담당
이처럼 제어의 권한이 프로그레머에서 외부 환경으로 역전되기 때문에 제어의 역전이 발생한다고 지칭

2. DI(의존성 주입)(Dependency Injection)

- 사용할 객체를 직접 생성하지 않음

- 외부 컨테이너가 생성한 객체를 주입받아 사용

A. 생성자를 통한 주입(레퍼런스 객체 없이는 객체 초기화할 수 없게 설계 가능)(추천)

Service service
 
@Autowired
public MainController(Service service){
           this.service = service;
}

          

B. 필드 객체 선언

@Autowired
Service service;

 

C. setter 메서드를 통한 의존성 주입(A와 다른 점만 표시)

Service service
 
@Autowired
private void setService(Service service){
           this.service = service
}

 

생성자 주입이 권장되는 이유
1. 명시적인 의존성 표시
2. 불변성
3. 의존성 주입 순환 참조 방지
4. 단위 테스트 용이성

특히 3, 4번이 주요하다.
3번은 SpringSecurity에서 암호화Bean을 사용하다 보면 자주 보는 문제이다.
- 생성자 주입을 사용하면 객체가 생성되기 전에 모든 의존성이 이미 주입되기 때문에 순환 참조 예방이 된다.
4번의 경우, 객체 생성 시 의존성을 생성자 매개변수로 전달 받으면 테스트할 때,
해당 의존성을 모의 객체로 대체하면 된다.
이를 통해 테스트가 외부 의존성에 영향을 받지 않고 독립적으로 실행될 수 있암(격리 보장)

몇 일 전에 만든 내가 만든 테스트코드는 열심히 외부에 의존하고 있었다...^^;

 

3. AOP(관점 지향 프로그래밍)(Aspect – Oriented Programming)

- OOP(객체지향 프로그래밍(Object – Oriented Programming)을 더욱 잘 사용하도록 돕는 개념

           (추상화 / 캡슐화 / 상숙 / 다형성)

- 어떤 기능을 구현할 때 핵심 기능과 부가 기능으로 구분하여 각각을 하나의 관점으로 보는 것

           (핵심 기능:        비즈니스 로직 별로 있는 고유한 기능)

           (부가 기능:        여러 비즈니스 로직에서 사용되는 반복적인 기능)

           --> 부가기능을 하나의 공통 로직으로 처리하도록 모듈화하여 삽입하는 방식

           (스프링에서는 프락시 패턴을 이용)

 

4. 경량 컨테이너 설계

- 스프링 프레임워크에는 다양한 모듈이 제공되지만 모든 모듈을 사용할 필요 X

--> 애플리케이션 개발에 필요한 모듈만 선택해서 사용할 수 있도록 설계됨(경량 컨테이너 설계)

 

 

 

스프링부트

- 스프링이 다양한 기능을 제공하는 만큼 설정이 복잡한데, 스프링부트를 사용하면 별도의 복잡한 설정 없이도 개발이 쉬워짐

 

부트 특징

1. 의존성 관리

- 스프링에서는 개발에 필요한 각 모듈의 의존성을 직접 설정 + 호환하는 버전을 명시해야 정상 동작

--> spring-boot-starter라는 의존성 제공을 통해 서로 호환되는 버전의 모듈 조합 제공

 

2. 자동 설정(Auto Configuration)

- 애플리케이션에 추가된 라이브러리를 실행하는데 필요한 환경 설정을 알아서 찾아 줌

--> 의존성만 추가하면 프레임워크가 자동으로 관리

Ex) SpringBootApplication annotationSpringBootConfiguration annotation, EnableAutoConfiguration annotation, ComponentScan annotation을 합친 구성

-> @ComponentScan@Component 시리즈(@Controller / @RestController / @Service / @Repository / @Configuration)이 붙은 클래스를 발견하여 @Bean 등록함

 

3. 내장 WAS(Web Application Server)

- spring-boot-starter-web은 톰캣 내장

--> 특별한 설정 없이 톰캣 실행 가능

 

4. 모니터링

- Spring Boot Actuator

 

 

 

 

서버간 통신

1. 단일서비스

- 하나의 사이트를 하나의 서비스 단위로 개발할 경우, 내부 메서드 호출 등을 통해 원하는 자원을 가져와 사용할 수 있음

- 한 기능을 유지보수할 때마다 사이트 전체 기능이 정지

2. MSA(Micro Service Architecture)

- 서비스 규모를 작게 나누면 기능별로 나눠서 개발

- 서비스 기능별로 구분해서 독립적인 애플리케이션을 개발하면 각 서비스 간에 통신해야 할 수 있음(블로그 기능 사용을 위해 로그인 기능을 사용하는 등)

3. 서버간 통신

- 한 서버가 다른 서버에 통신을 요청(TCP/IP, HTTP/HTTPS, SOAP)

 

개인적으로 이번에 만들었던 팀프로젝트에서 느꼈던 아쉬운 점이다.
단일서비스로 이루어졌기 때문에 한 곳에서 에러가 터지면 다같이 셧다운되는 문제가 종종 발생했다.
로그인에서 에러가 났더니 다같이 멈췄다던지, application.properties가 없어서 실행이 안 된다던지...
하지만 아무래도 팀 내 여러 명으로 구성된 파트가 제대로 개발하기 힘들 것 같기도 하다...
아무튼 아쉬운 부분!
다음에는 MSA가 실현된 프로젝트를 개발했으면 하는 부분이 있다.

 

스프링 부트의 동작 방식

- spring-boot-stater-web 모듈을 사용하면 기본적으로 톰캣을 사용하는 스프링 MVC 구조를 기반으로 동작

1. 서블릿: 클라이언트의 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술

--> 서블릿 컨테이너에서 관리

 

2. 서블릿 컨테이너

- 서블릿 인스턴스를 생성하고 관리하는 역할을 맡은 주체

- 서블릿 객체의 생성, 초기화, 호출, 종료하는 생명주기를 관리

- 서블릿 객체를 싱글톤 패턴으로 관리

(유일한 인스턴스 / 전역 접근 / 지연초기화(인스턴스가 필요한 시점에 생성) / 스레드 안전성)

 

3. DispatcherServlet

- 스프링의 서블릿 역할을 수행

- 스프링에서는 톰캣을 임베드해서 사용

--> 서블릿컨테이너와 DispatcherServlet은 자동 설정된 web.xml의 설정값을 공유

- 동작 순서(@Controller)

A. DispatcherServlet으로 HttpServletRequest

B. Handler Mapping을 통해 요청 URI에 매핑된 핸들러(컨트롤러) 탐색

C. HandlerAdapter로 컨트롤러 호출

D. HandlerAdapter에 컨트롤러 응답이 돌아오면 ModelAndView로 응답을 가공하여 반환

D-2 View 형식으로 리턴하는 컨트롤러일 경우, ViewResolver를 통해 View를 받아 리턴

- 동작 순서(@RestController)

A. DispatcherServlet으로 HttpServletRequest

B. Handler Mapping을 통해 요청 URI에 매핑된 핸들러(컨트롤러) 탐색

C. HandlerAdapter로 컨트롤러 호출

D. HandlerAdapter에 컨트롤러 응답이 돌아오면 MessageConverter로 응답을 가공하여 반환

E. HTTP 응답

 

 

레이어드 아키텍처(Layered Architecture)

- 컴포넌트를 유사 관심사를 기준으로 수평적으로 구성한 구조

- 프레젠테이션 계층:      클라이언트의 요청을 해석하고 응답

                                UI / API 제공

                                비즈니스 계층으로 요청을 위임하고 받은 결과를 응답하는 역할만 수행

- 비즈니스 계층:            애플리케이션이 제공하는 기능을 정의

                                세부 작업을 수행하는 도메인 객체를 통해 업무를 위임

- 데이터 접근 계층:        데이터베이스에 접근하는 일련의 작업 수행

* MVC 패턴일 경우(Model – View – Controller)

- 프레젠테이션 계층:      ~ 컨트롤러:                        클라이언트와 접점

- 비즈니스 계층:            서비스:                                  핵심 비즈니스 로직+트랜잭션 처리+유효성 검사

- 데이터 접근 계층:        DAO(Data Access Object):           DB에 접근

 

 

디자인 패턴

- 애플리케이션 개발에서 발생하는 문제는 유사한 경우가 많음

--> 동일한 해결책 적용 가능

1. 생성 패턴

- 객체 생성에 사용되는 패턴

- 객체를 수정해도 호출부가 영향을 받지 않게 함

 

2. 구조 패턴

- 객체를 조합하여 더 큰 구조를 만드는 패턴

 

3. 행위 패턴

- 객체 간의 알고리즘 / 책임 분배에 관한 패턴

- 객체 하나로는 수행할 수 없는 작업을 여러 객체를 이용하여 작업 분배

- 결합도 최소화 고려

 

해당 패턴 중 몇 개는 우리 프로젝트에서 아쉬웠던 부분을 해결해줄 수 있을 것 같았다.
프로젝트에서 일부분이라도 적용해볼 예정
 

Rest API

- Rest(Representational State Transfer):       분산 하이퍼미디어 시스템 아키텍처의 한 형식

                                                     주고 받는 자원(Resource)에 이름을 규정하고 uri에 명시

                                                     -> HTTP 메서드를 통해 해당 자원의 상태를 주고 받음

- API(Application Programming Interface):   애플리케이션에서 제공하는 인터페이스

                                                     Rest 아키텍처를 따르는 시스템 / 애플리케이션 인터페이스

                                                     API를 통해 서버 / 프로그램 사이를 연결

- 특징

1. 유니폼 인터페이스:      일관된 인터페이스

                                - 어떤 프로그래밍 언어로 만들었는지와 상관없이 호환

2. 무상태성:                  서버에 상태 정보를 따로 보관하지 않음

                                - 클라이언트가 여러 요청을 보내도 개별적으로 처리(쿠키나 세션에 보관X)

                                --> 서버가 불필요한 정보를 관리하지 않아 비즈니스 로직 자유도 증가 + 단순화

3. 캐시 가능성:             HTTP 표준을 그대로 사용하기 때문에 HTTP 캐싱 기능 적용 가능

                                - 이전 검색한 리소스를 로컬 저장소에 저장

                                - 동일 리소스에 대한 후속 요청은 서버에 다시 요청하지 않고 캐시된 버전 사용

                                응답과 요청 모두에서 캐싱 가능한지 명시가 필요함

                                --> 서버의 트랜잭션 부하가 줄어 효율적

4. 레이어 시스템:           Rest 서버는 네트워크 상의 여러 계층으로 구성

                                클라이언트는 서버와 연결되는 포인트(프레젠테이션 계층)만 알면 됨

5. 클라이언트-서버 아키텍처:       서버는 API / 클라이언트는 사용자 정보를 관리하는 구조로 분리 설계

                                           - 서로에 대한 의존성을 낮춤

- 설계 규칙(URI)

1. 마지막에 /를 포함하지 않음

2. 리소스가 길어지면 사용(언더바 X)

3. 결과(명사)를 포함(행위(동작)X)

4. 소문자로 작성

5. 파일의 확장자는 포함 X

 

우리 프로젝트는 설계규칙이 제대로 지켜지지 않았다...고치고 싶지만 시간이 부족하니 어쩔 수 없네

'이해' 카테고리의 다른 글

클린 코드  (0) 2024.04.30
간단한 IT 용어 정리(프로토콜/HTTP/아키텍처/REST)  (0) 2024.04.30
Spring Security의 동작 원리 + JWT + Oauth2까지  (0) 2024.04.19
상태코드  (0) 2024.02.23
데이터 형식 주요 어노테이션  (0) 2024.02.23