일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- mysql
- spring cloud
- web server
- 배포
- Spring Security
- spring batch
- 도커
- 스프링 부트
- 컨테이너
- 가상화
- HTTP
- vm
- Java
- Container
- computer science
- 자바
- Spring
- ORM
- 웹 서버
- spring boot
- JPA
- 백엔드
- 데이터베이스
- 영속성 컨텍스트
- CI/CD
- 스프링
- 스프링 시큐리티
- 스프링 배치
- virtualization
- CS
- Today
- Total
개발 일기
[스프링 부트3 백엔드 개발자 되기] 스프링 총 정리 본문
스프링과 스프링 부트 차이
스프링
은행 시스템과 같이 몇 백만, 몇 천만의 사람이 한꺼번에 잔고 조회를 하고, 입금 및 출금 요청을 처리하는 등 많으 유저의 요청을 동시에 처리해야하는 엔터프라이즈 애플리케이션는 성능, 안정성 그리고 보안이 매우 중요해 졌다. 그러나 이러한 것들을 고민하며 비즈니스 로직까지 개발하는 것은 비효율적이였기에 이러한 엔터프라이즈 애플리케이션을 위한 개발 환경을 제공해서 기능 개발에만 집중할 수 있도록 하기 위해 스프링 프레임워크를 제공했고 개발자들은 비즈니스 로직에만 집중할 수 있게 됐다.
스프링 부트
그러나 설정이 매우 복잡하다는 단점이 있었고 이러한 단점을 극복하여 스프링 프레임워크를 더 쉽고 빠르게 이용할 수 있도록 만든 도구가 스프링 부트이다. 빠르게 스프링 프로젝트를 설정할 수 있고 의존성을 관리하기 쉬워졌다.
주요한 특징은 아래와 같다.
- 톰캣(Tomcat), 제티(Jetty), 언더토우(Undertow) 같은 WAS가 내장되어 있어 별도의 설치하지 않고 독립적으로 실행 가능
- 빌드 구성을 단순화하는 스프링 부트 스타터를 제공(ex: spring-boot-starter-web)
- XML이 아닌 자바 코드로 작성 가능
- 애플리케이션을 JAR 파일로 패키징하여 쉽게 배포하고 실행
- 애플리케이션 모니터링 및 관리 도구인 스프링 액츄에이터 제공
스프링 부트 스타터
스프링 부트 스타터는 의존성을 모아 놓은 그룹인데 이를 통해 필요한 기능을 간편하게 설정할 수 있다.
spring-boot-starter-{작업유형} 이라는 명명 규칙이 있고 이 네이밍을 사용하는 것이 스프링 부트 스타터이다.
- spring-boot-starter-web
- spring-boot-starter-test
- spring-boot-starter-validation
- spring-boot-starter-actuator
- spring-boot-starter-data-jpa
가 있다.
스프링 부트는 스프링에 속한 도구.
스프링 | 스프링 부트 | |
목적 | 엔터프라이즈 애플리케이션 개발을 더 쉽게 | 스프링의 개발을 더 쉽고 빠르게 |
설정 파일 | 개발자가 수동으로 구성 | 자동으로 구성 |
XML | 일부 파일은 XML로 직접 생성 | 사용 X |
인메모리 데이터베이스 지원 |
지원하지 않음 | 인메모리 데이터베이스 자동 설정 지원 |
서버 | WAS 수동 설정 | 내장형 서버 제공. 별도 설정 필요 X |
스프링 컨셉
스프링은 모든 기능의 기반을 제어의 역전과 의존성 주입에 두고 있다.
1.1 - 제어의 역전(Inversion of Control)
IoC는 Inversion of Control의 약어로 "제어의 역전"이다.
원래는 자바에서 객체를 생성할때 필요한 곳에서 new 키워드를 통해 직접 생성을 했을 것이다.
public class A {
B b = new B(); // A가 B 객체를 직접 생성
}
제어의 역전은 다른 객체를 직접 생성하거나 제어하는 것이 아니라 외부에서 관리 중인 객체를 가져와 사용하는 것이다.
public class A {
private B b; // B 객체를 직접 생성하지 않음
// B 객체는 외부에서 주입(Injection)됨
}
이전과 다르게 클래스 B 객체를 직접 생성하는 것이 아니라 스프링 컨테이너에서 관리 중인 객체를 받아와 변수 b에 할당하는 것이다.
객체의 생성과 의존성 관리의 제어권이 개발자의 손에서 스프링 컨테이너로 넘어갔기 때문입니다. 즉, 객체의 라이프사이클과 의존성 관리를 더 이상 개발자가 제어하지 않고, IoC 컨테이너가 대신 처리
1.2 - 의존성 주입(Dependency Injection)
public class A {
@Autowired
private B b; // A에서 B를 주입 받는다.
}
DI는 어떤 클래스가 다른 클래스에 의존하는 것으로 위의 코드가 IoC/DI의 예시이다.
@Autowired라는 어노테이션으로 스프링 컨테이너에 있던 "빈"을 주입하는데 이때 빈은 스프링 컨테이너에서 관리하는 객체이다.
즉, 스프링은 클래스 A에서 B 객체를 쓰고 싶다면 객체를 직접 new 키워드를 통해 생성하는 것이 아니라 스프링 컨테이너에서 객체를 주입 받아 사용한다.
스프링 컨테이너란?
스프링 빈을 생성하고 관리하는 객체로 빈이 생성되고 소멸되기 까지의 생명주기(라이프 사이클)를 관리한다.
또한 위에서 봤듯이 빈을 주입할 수 있도록 의존성 주입(DI)를 지원한다.
개발자가 직접하는 것이 아니라 자동으로 처리해준다.
스프링 빈이란?
스프링 컨테이너가 생성하고 관리하는 객체로 XML 설정파일 또는 @Component 어노테이션을 통해 스프링 컨테이너에 빈을 등록할 수 있고 빈은 스프링 컨테이너에 의해 싱글톤으로 관리된다.
싱글톤이란?
스프링 컨테이너가 해당 빈의 인스턴스를 하나만 생성하여 애플리케이션 내에서 공유한다는 의미이다.
즉, 스프링 애플리케이션 전체에서 빈의 인스턴스가 하나만 존재하며, 동일한 빈을 필요로 하는 곳에서는 항상 같은 객체가 제공된다.
이때 Multi-Thread 환경에서 다중 요청 처리 방식에 대해서는 https://wildeveloperetrain.tistory.com/333 해당 블로그를 참고하면 좋을 것 같다.
2. 관점 지향 프로그래밍(Aspect Oriented Programming)
스프링을 처음 공부하면서 이해하기 어려웠던 개념 중 하나인 관점 지향 프로그래밍 AOP이다. 프로그래밍에 대한 관심을 핵심 관점, 부가 관점으로 나누어서 공통 관심사를 기준으로 모듈화하는 것을 의미한다.
대표적인 예로 로깅이 있다. 계좌 이체, 고객 관리하는 프로그램이 있을 때 각 프로그램에는 로깅 로직, 즉, 지금까지 벌어진 일을 기록하기 위한 로직과 여러 데이터를 관리하기 위한 데이터베이스 연결 로직이 포함된다.
이때 핵심 관점은 계좌 이체 및 고객 관리 로직이고 부가 관점은 로깅 및 데이터베이스 연결 로직일 것이다.
여기에 AOP를 적용하게 되면 부가 관점에 해당하는 로직을 모듈화해서 중복 되는 부가 관점 코드를 핵심 관점 로직에서 분리할 수 있게 된다.
이를 통해 프로그램 변경 및 확정 시에 유연하게 대응 가능하다.
3. 이식 가능한 서비스 추상화(Portable Service Abstraction)
이것이 내가 가장 이해하기 어려웠던 스프링 컨셉이였다. 책의 내용으로 충분하지 않다고 판단해 추가적으로 내용을 긁어왔다.
PSA란 환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조를 말한다.
이는 POJO 원칙을 철저히 따른 Spring의 기능으로 Spring에서 동작할 수 있는Library들은 POJO원칙을 지키게끔 PSA형태의 추상화가 되어있음을 의미한다.
"잘 만든 인터페이스 하나가 열 클래스 부럽지 않다"
PSA = 잘 만든 인터페이스
PSA가 적용된 코드라면 나의 코드가 바뀌지 않고, 다른 기술로 간편하게 바꿀 수 있도록 확장성이 좋고, 기술에 특화되어 있지 않는 코드를 의미한다.
Spring은 Spring Web MVC, Spring Transaction, Spring Cache 등의 다양한 PSA를 제공한다.
https://dev-coco.tistory.com/83 이 블로그를 참고하면 좋을 것 같다.
스프링 부트 3과 호환되는 Java
스프링 부트 2는 자바 8 버전 이상을 사용했고 스프링 부트 3은 17버전 이상을 사용해야 한다. 이때 자바 17의 주요 변화에 대해 살펴보자.
텍스트 블록
여러 줄의 문자열을 쉽게 다룰 수 있도록 지원. 여러줄의 문자열을 """로 감싸서 작성 가능하며 이스케이프 문자 필요 없어졌다.
// JAVA 17 이전
String json = "{\n" +
" \"name\": \"John\",\n" +
" \"age\": 30,\n" +
" \"city\": \"New York\"\n" +
"}";
// JAVA 17 이후
String json = """
{
"name": "John",
"age": 30,
"city": "New York"
}
""";
formatted() 메소드
// JAVA 17 이전
String name = "John";
int age = 30;
String formattedString = String.format("Name: %s, Age: %d", name, age);
System.out.println(formattedString);
// JAVA 17 이후
String name = "John";
int age = 30;
String formattedString = "Name: %s, Age: %d".formatted(name, age);
System.out.println(formattedString);
레코드(record)
DTO를 선언할때 주로 사용되며 데이터 전달을 목적으로 하는 객체를 더 빠르고 간편하게 만들기 위한 기능을 제공한다.
- 상속 불가
- 파라미터에 정의한 필드는 자동으로 private final로 정의됨
- Getter 자동 생성
- 클래스 정의, 생성자, equals(), hashCode(), toString() 자동으로 생성
패턴 매칭
타입 확인을 위해 사용하던 instanceof 키워드를 조금 더 쉽게 사용할 수 있게 됐다.
이전에 instanceof 키워드와 형변환 코드를 조합해야 했지만 이제는 바로 형변환을 한 다음에 사용할 수 있다.
// 11버전
if (o instanceof Integer) {
Integer i = (Integer) o
}
// 17버전
if (o instanceof Integer i) {
}
자료형에 맞는 case 처리
Object obj = 42;
switch (obj) {
case Integer i -> System.out.println("Integer: " + i);
case String s -> System.out.println("String: " + s);
default -> System.out.println("Unknown type");
}
Servlet, JPA의 네임 스페이스가 Jakarta로 대체
스프링 부트 3에서는 Java EE 명세가 Jakarta EE로 바뀌면서 네임스페이스가 javax에서 jakarta로 변경
GraavlVM 기반의 스프링 네이티브 공식 지원
스프링 부트 3에서는 GraalVM을 기반으로 네이티브 이미지를 생성하여, 기존에 JVM에 구동되는 어플리케이션에 비해 더 빠른 시작 시간과 더 적은 메모리 사용량을 제공하는 네이티브 실행 파일을 만들 수 있다.
@SpringBootApplication 이해하기
스프링 부트 애플리케이션의 시작 지점을 정의하는 어노테이션으로 핵심적인 어노테이션 중 하나이다. 실제로 들여다 보면 아래와 같다.
여기서 아래 3가지 어노테이션인 @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan에 집중할 필요가 있다.
@SpringBootConfiguration
스프링 부트 애플리케이션의 설정 클래스를 나타내는 어노테이션으로 내부에 @Configuration을 포함하고 있어 빈으로 등록된다.
단순히 스프링 빈 설정 클래스임을 의미하는 @Configuration보다 더 구체적으로 스프링 부트 애플리케이션을 위한 설정 클래스
@ComponentScan
사용자가 등록한 빈을 읽고 등록하는 애노테이션이다. @Component라는 애노테이션을 가진 클래스들을 지동으로 찾아 빈으로 등록하는 역할을 한다.
그렇다고 등록하고자 하는 모든 빈에 @Component를 달아야하는 것은 아니다.
어노테이션 명 | 설명 |
@Configuration | 설정 파일 등록 |
@Repository | ORM 매핑 |
@Controller, @RestController | 라우터 |
@Service | 비즈니스 로직 |
이 어노테이션들을 타고 들어가 선언부를 보면 @Component를 포함하고 있기 때문에 위 어노테이션들만 사용하면 자동으로 해당 클래스는 빈으로 등록된다.
예를 들어 @Service어노테이션을 보면 @Component를 포함하고 있다.
@EnableAutoConfiguration
자동 구성을 활성화하는 애노테이션으로 스프링 부트 서버가 실행될 때 스프링 부트의 메타 파일을 읽고 정의된 설정들을 자동으로 구성하는 역할을 수행한다. 클래스패스에서 필요한 라이브러리들을 보고 스프링 부트가 적절한 설정을 자동으로 적용해준다.
- 스프링 부트가 애플리케이션을 실행할 때, 클래스패스에 존재하는 라이브러리들을 확인
- 클래스패스에 있는 라이브러리에 따라, 스프링 부트는 해당 라이브러리에 맞는 자동 설정을 제공합니다. 예를 들어, H2 데이터베이스 라이브러리가 클래스패스에 존재하면, 스프링 부트는 자동으로 데이터베이스 연결 설정을 구성
ORM
ORM(Object-Relational Mapping)은 자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법이다. 이는 데이터베이스의 값을 객체로 다룰 수 있는데 이는 SQL을 몰라도 자바 언어로 쿼리를 다룰 수 있게 되는 것이다.
ORM에 여러 종류가 있는데 자바에서는 JPA(Java Persistence API)를 표준으로 한다. JPA 자체는 RDB를 사용하는 방식을 정의한 인터페이스라서 실제 사용을 위해서는 ORM 프레임워크를 추가로 선택해야한다. 대표적으로는 하이버네이트(Hibernate)를 많이 사용한다.
즉, 하이버네이트는 JPA 인터페이스를 구현한 구현체인 자바 ORM 프레임워크이다. 이 프레임워크는 내부적으로 JDBC API를 사용한다.
엔티티 매니저와 영속성 컨텍스트
엔티티(Entity)
데이터베이스의 테이블과 매핑되는 객체
엔티티 매니저(EntityManager)
엔티티를 관리해 데이터베이스와 어플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할. 그리고 이런 엔티티 매니저를 만드는 곳이 엔티티 매니저 팩토리(Entity Manager Factory)
스프링 부트는 내부에서 엔티티 매니저 팩토리를 싱글톤으로 관리하고 의존성을 주입받아 사용한다.
동시성 문제
스프링 부트는 기본적으로 빈은 하나만 생성해서 공유하여 동시성 문제가 발생할 수 있다. 그래서 실제로 엔티티 매니저가 아닌 실제 엔티티 매니저와 연결하는 프록시 엔티티 매니저를 사용한다. 그리고 DB 트랜잭션과 관련된 실제 엔티티 매니저를 호출한다.
영속성 컨텍스트(Persistence Context)
영속성 컨텍스트는 엔티티를 관리하는 가상의 공간이다. 이때 주요한 특징들이 있다.
- 1차 캐시
: 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다. 이때 캐시의 키는 엔티티의 @Id 애노테이션이 달린 기본키 역할을 하는 식별자이고 값은 엔티티이다. 엔티티를 조회할 때 1차 캐시에서 먼저 데이터를 조회하고 값이 있으면 반환하고 값이 없으면 데이터베이스에서 조회하여 1차 캐시에 저장 후 반환한다. - 쓰기 지연
: 트랜잭션을 커밋하기 전 까지는 데이터베이스에 실제로 쿼리를 보내지 않고 모았다가 커밋 시에 한번에 실행한다. 만약 여러 쿼리가 쌓인 상태에서 커밋 된다면 이 쿼리 들을 적당한 묶음으로 쿼리를 요청하면 성능을 개선할 수 있다. - 변경 감지
: 트랜잭션 커밋 시에 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있으면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영한다. 이 또한 쿼리들을 적당한 묶음으로 처리하여 성능 개선을 이끌 수 있다. - 지연 로딩
: 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩하는 것이 아니라 실제로 사용될 때 쿼리를 날려 데이터를 조회한다.
엔티티의 상태
- 비영속 상태(transient)
엔티티를 처음 생성하면 엔티티는 비영속 상태 - 관리 상태(managed)
: em.persist() 메소드를 사용하여 영속성 컨텍스트에서 상태가 관리된다. - 분리 상태(detached)
: 만약 엔티티를 영속성 컨텍스트에서 관리하고 싶지 않다면 em.detach()메소드를 통해 분리상태로 만든다. - 삭제 상태(removed)
: 더 이상 객체가 필요 없다면 remove()메소드를 사용해 엔티티를 영속성 컨텍스트와 DB에서 삭제
Spring Security
인증(Authentication)은 유저의 신원을 입증하는 과정으로 로그인하는 과정을 예로 들 수 있고
인가(Authorization)은 사이트의 특정 부분에 접근할 수 있는지에 대한 권한을 확인하는 작업이다.
스프링 시큐리티는 스프링 기반 애플리케이션의 보안을 담당하는 스프링 하위 프레임워크이다. CSRF공격, Session Fixation 공격을 방어해주고 요청 헤더도 보안 처리를 해줘서 보안에 있어서 개발자의 부담을 크게 줄여 준다.
이때 스프링 시큐리티는 필터 기반으로 동작한다.
위와 같이 다양한 필터들이 각각 인증과 인가와 관련된 작업을 처리한다.
SecurityContextPersistenceFilter부터 시작해서 아래로 내려가면서 FilterSecurityInterceptor까지 순서대로 필터를 거치며 빨간 화살표로 연결된 오른쪽 박사의 클래스를 거치며 실행된다.
원하면 특정 필터를 제외 시키고 필터 앞 뒤로 커스텀한 필터를 삽입할 수도 있다.
'도서' 카테고리의 다른 글
[이더리움을 활용한 블록체인 프로젝트 구축] 블록체인 개념 잡기 (0) | 2024.09.15 |
---|