어플리케이션을 개발하는 프레임워크. Spring을 사용할 경우 설정 해야할 옵션이 많은데 Spring은 최소한의 설정 만으로 어플리케이션을 생성하여 사용 가능.
Tomcat, Jetty등 웹서버를 포함하고 있어 별도의 웹 서버를 설치하지 않아도 바로 실행 가능.
IOC, AOP 스프링 기능을 사용하기 위해 별도의 설정을 해줄 필요 없이 사용 가능.
모니터링등 다양한 라이브러리와 툴 제공.
aplication.yml 이나 application.properties 파일을 사용한다. 확장자가 다른 것 빼고는 같다.
초기 스프링부트 프로젝트 생성시 application.properties 파일이었다. 확장자를 .yml 파일로 변경하여 사용할 수 있다.
Tomcat, Jetty등 웹서버를 포함하고 있어 별도의 웹 서버를 설치하지 않아도 바로 실행 가능.
IOC, AOP 스프링 기능을 사용하기 위해 별도의 설정을 해줄 필요 없이 사용 가능.
모니터링등 다양한 라이브러리와 툴 제공.
백엔드에서 Dispatch란 의미는 사용자의 요청을 받아서 처리할 수 있는 개체에 전달한다는 의미이다.
Dispatcher는 클라이언트의 모든 요청을 처음 받아서 적절한 핸들러에 요청을 전달한다. 이 핸들러는 Controller나 Handler Mapping이 될 수 있다. 핸들러가 요청을 처리하면 이 결과를 Dispatcher가 Http Response 형태로 만들어서 반환하다. Dispatcher는 일종의 게이트웨이 역할을 수행한다.
Dispatcher를 통해 백엔드는 사용자의 요청을 중앙 집중적으로 처리 가능하다.
DispatcherServlet ->
/
Controller 클래스 파일을 생성한 후 어노테이션(@RestController) 추가한 후 REST API로 맵핑할 함수를 구현한뒤 위에 어노테이션(@GetMapping)을 추가하면 된다.
객체를 반환하면 JSON으로 변환해서 반환한다. 라이브러리를 사용하면 XML로 변경하여 반환 하는것도 가능하다.
private UserDaoService service = new UserDaoService();
이런 식으로 직접 객체를 생성해서 사용하면 안된다. UserDaoService의 객체는 언제든 변경될 수 있다. 의존성을 주입하여 변경에 대응해야 하며 스프링 프레임워크는 이런 의존성 주입에 특화되어 있다. 다른 프레임워크 역시 마찬가지 일 것이다. 스프링에서 객체를 생성하여 관리되는 객체를 'Bean'이라고 한다. Controller 역시 스프링프레임워크에서 생성한 Bean이다.
웹 어플리케이션 프레임워크를 사용한다면 의존성 주입을 어떻게 하는지, 어떤 객체들을 프레임워크를 통해 생성해야 하는지 구분할 수 있어야 할것이다.
이런 의존성 주입 과정은 어플리케이션 실행시 로그로도 출력된다.
UserController 생성자에서 UserDaoService 객체를 의존성 주입으로 받으며 이 과정을 실행 로그에서 확인 가능 하다. UserDaoService의 경우 의존성을 주입받는 객체라는 것을 명시하기 위해 Service 어노테이션을 지정 하였다
REST API 호출한 후 적절한 결과값을 반환해야 하는데 프레임워크는 이를 구현하기 쉽게 기능을 제공한다.
아래는 스프링에서 POST API 호출에 대한 결과를 ResponseEntity를 사용하여 반환하도록 구현하였다. 이를 통해 200 OK 응답 대신 201 Created와 Header에 추가된 데이터를 확인할 수 있는 URI가 포함된다.
왜 추가된 데이터를 확인할 수 있는 URI를 반환할까 ? 만약 사용자가 추가된 데이터에 대해 확인 하고 싶다면 전체 데이터를 조회하는 API를 호출해야 하며 이는 데이터 낭비뿐만 아니라 동기화 문제도 발생할 수 있다. 하지만 추가된 데이터에 대해 조회할 수 있는 URI를 전달하면 전체 조회 없이 해당 URI를 통해 바로 새로 추가된 데이터를 확인할 수 있다.
API 호출시 예외를 처리하지 않으면 다음과 같이 소스코드를 반환할 수 있기에 프레임워크에서 예외가 발생할 경우 어떤 응답값을 반환하는지 주의해야 한다
웹 어플리케이션에서 Controller가 비즈니스 로직을 수행하기 전에 수행해 주어야 하는 공통적인 비즈니스 로직을 실행하는 기능이 AOP이다.
이 AOP를 이용해서 예외를 처리하는 Controller를 만들 수 있다. 이 Controller는 다른 Controller에서 예외가 발생할 경우 AOP 기능을 통해 예외를 대신 처리할 수 있다. 이를 통해 각 Controller마다 예외 처리가 아닌 예외 처리 Controller 한곳에서 로직을 정리할 수 있다.
아래는 AOP를 위해 ControllerAdvice 어노테이션을 추가하고 UserNotFoundException 예외가 발생할 경우 처리하는 핸들러 함수 구현 모습니다
사용자의 입력값은 반드시 유효성 검사가 필요하다. 유효성 검사를 직접 코드로 검사할 수 있지만 프레임워크에서 유효성 검사를 지원하는 기능이 있을 수 있다. Java에서는 언어단에서 값의 유효성을 검사할 수 있는 것 같다. 어노테이션을 통해서 멤버 변수에 대한 조건을 설정할 수 있다.
유효성을 검사 기능을 사용하는 부분. 역시 여기서 어노테이션만 지정하면 된다
잘못된 인자가 들어올 경우 발생하는 예외 처리도 추가하였다
클라이언트 프로그램 뿐만 아니라 백엔드 어플리케이션 역시 다국어 처리가 필요하다
스프링 프레임워크에서 다국어를 지원하는 Bean을 지원하고 이 Bean은 어플리케이션 시작할때 등록할 수 있도록 한다
스프링 프레임워크에서도 다국어를 처리할때 다국어 파일을 사용한다
어플리케이션 설정 파일에 다국어 파일 관련 설정 추가
messageSource의 Autowired 어노테이션으로 의존성 주입을 받으며 messageSource에 "greeting.message"라는 키를 이용하여 언어 리소스를 가져온다
REST API 호출시 클라이언트는 대부분 JSON 데이터로 응답 받기를 원한다. 하지만 데이터 포맷이 XML이나 다른 포맷으로도 요청을 할 필요가 있을 것이다. 백엔드 시스템에서는 클라이언트의 데이터 포맷 요청에 대한 처리도 필요하다.
헤더에 Accept 라는 키값을 지정하여 원하는 데이터 포맷을 지정할 수 있다. 여기서 백엔드가 지원하지 않는 데이터 포맷이라면 406 에러가 발생한다.
클라이언트 요청시 기본은 JSON이지만 명시적으로 JSON이라고 지정할 수 있다
프레임워크에서 데이터 포맷을 지원하는 기능이 있는지 확인한 후 필요하다면 처리할 수 있도록 해야 할 것이다. 스프링 부트에서는 단순히 라이브러리를 추가만 하면 다국어를 처리할 수 있었다.
스프링 부트의 경우 장비 조립하듯 어플리케이션을 만드는 것 같다. 대부분의 사람들이 이 모든게 의존성 주입때문에 가능한 것이라고 말하는 것 같은데 좀더 정확히는 의존성 주입과 백엔드 시스템의 요구 기능이 고착화 되기에 가능한 것 같다.
백엔드 시스템에서 관리하고 있는 데이터중 클라이언트에게 노출되면 안되는 데이터가 있을 수 있다. 이를 각 API 마다 노출되지 않도록 처리할 수 있지만 프레임워크를 사용하면 시스템 전체에 노출되면 안되는 데이터를 설정할 수 있다.
노출되면 안되는 데이터가 추가한 후 아무런 처리도 하지 않으면 그대로 데이터가 노출 될것이다.
간단한 방법으로는 Json 파싱시 무시하도록 설정할 수 있을 것이다. 어노테이션으로 Json 파싱을 무시하도록 처리 가능하다.
클래스 범위 어노테이션에서도 지정 가능
이렇게도 처리 가능한데 만약 개발 서버라서 데이터를 의도적으로 노출해야 되는 상황이라면 다시 빌드해야 하기에 번거로울 것이다. 또는 보이지 않도록 처리하는 것이 아니라 *** 문자로 변환해서 표현 하고 싶을 수 도 있을 것이다.
스프링부트에서는 필터를 사용하여 특정 필드의 표시를 제어할 수 있다.
admin을 path에 추가해서 요청 하도록 하였다.
http://localhost:8088/admin/users
User에 UserInfo 어노테이션이 붙으면 이 User를 반환할때 MappingJacksonValue로 반환해야 하며 그렇지 않을 경우 에러 발생한다
백엔드 시스템은 REST API 버전 관리가 필요하다. REST API 설계가 바뀔경우 버전 관리를 통해 사용자에게 API를 제공한다.
이는 카카오나 페이스북 같은 회사도 수행하고 있다. 사용자나 개발자에게 어떤 API를 사용하는지 가이드라인을 주기위해 필요하다.
v2 path를 추가하여 구분
REST API 응답값에 개발자에게 정보를 제공하기 위한 데이터를 쉽게 추가할 수 있도록 도와주는 기능인 것 같다
HATEOAS 라이브러리 추가 후 REST API 핸들러 함수에서 응답 데이터에 개발 관련 데이터를 추가할 수 있다.
사용자 하나에 대한 조회를 하면 응답으로 전체 사용자 조회 관련 API 정보를 반환하도록 한다. 여기서 전체 사용자 조회 REST API 경로 관련 정보는 어떤것도 기록하지 않으며 단지 연관된 함수 정보만 넘긴다.
REST API를 호출하면 알아서 연관된 REST API 경로 주소가 같이 응답으로 전달된어 온다.
개발 과정중 필요한 응답 정보 역시 사용자에게 필요한 정보 만큼 많이 중요해 보인다. 이에 대한 대응도 생각해야 할 것이다.
body가 아닌 header로 추가 데이터를 받을 수 도 있다.
백엔드 시스템을 사용하는 여러 플랫폼의 클라이언트(모바일, 데스크탑, 프론트 엔드, ...) 개발자는 백엔드 시스템의 API를 사용할 것이다. API를 제공하는 입장인 백엔드 시스템에서 문서화 역시 매우 중요한 작업이다. 프레임워크에서는 문서하를 자동으로 처리 해줄 수 있는 라이브러리를 제공한다.
스프링부트에서는 swagger를 사용하여 문서화 가능 하다. swagger 역시 Bean으로 등록하면 알아서 문서를 볼 수 있는 데이터를 생성해준다
서버를 구동하면 swagger가 작성한 문서 데이터를 웹 브라우저를 통해 확인 가능
이 데이터를 프론트엔드에서 처리하여 UI로 표시할 수 있고 다른 외부 Tool을 사용하여 UI로 확인 할 수 있다. swagger 추가시 swagger-ui까지 같이 추가하면 UI를 자동으로 구현하여 보여준다.
백엔드 시스템 개발시 문서화를 직접 하기보다 프레임워크와 라이브러리를 사용하여 자동으로 생성할 수 있는 전략을 생각해야 한다.
// 프레임워크에 대한 정보 커스터마이징
User DTO에 대한 정보 커스터마이징
기존 : User DTO
커스터마이징 : User DTO
백엔드 시스템은 서버의 상태를 확인할 수 있는 API가 필요하다. 이 API를 직접 구현할 수 도 있지만 라이브러리를 사용하면 자동으로 구현 가능하다.
스프링부트에서 actuator 라이브러리를 추가하면 바로 상태를 확인할 수 있는 API를 구현해준다.
서버의 상태 확인
http://localhost:8088/actuator/health
개발자에게 API 검색 기능을 제공하는 라이브러리. 개발자가 웹 어플리케이션 서버에 접속하면 API를 검색할 수 있는 페이지를 제공.
액세스 토큰, Auth 등을 사용하여 인증된 사용자만 REST API를 호출할 수 있도록 해야한다.
스프링 부트에서는 spring-security를 사용하면 REST API 호출할때 username과 password를 입력받아 검증할 수 있다.
패스워드 직접 지정
JPA(Java Persistence API) : 자바 ORM 기술에 대한 API 스펙이며 이를 구현하는 다양한 구현체 라이브러리(대표적으로 Hibernate)가 있다. 관계형 데이터를 관리하는 API들이 있으며 EntityManager를 통해 CRUD 처리한다.
Hibernate : JPA의 구현체, 인터페이스를 직접 구현한 라이브러리, 자바에서 가장 많이 사용하는 ORM 라이브러리. DB의 Entity와 ORM 객체를 자동으로 맵핑해주는 기능을 가진 라이브러리. 생산성, 유지보수, 관계형 DB에 대한 종속성을 줄일 수 있음. JPA를 관리하는 클래스의 집합체. 매우 오랫동안 사용되어 왔고 안정성과 성능면에서 검증됨.
Spring Data JPA : 스프링의 모듈. JPA를 추상화한 Repository 인터페이스 제공.
제일 밑에 관계형 데이터베이스 있고 그 위에 Hibernate를 사용하여 JPA가 구현되어 있다. 그리고 이 JPA를 사용하기 쉽게 Spring Data JPA로 추상화되어 있다.
JPA를 사용을 위해 필요한 라이브러리 추가와 설정 파일 수정을 해준다
h2-console 접속
ID/PW는 security에서 설정한 ID/PW 입력
인증을 비활성화 하는 방법은 WebSecurityConfigurerAdapter 멤버 함수를 오버라이드 해야한다
h2 console에 접속한 모습
JPA를 사용하면 클래스를 통해 자동으로 DB를 생성할 수 있다. 클래스에 DB에 추가될 데이터 모델을 어노테이션으로 지정한 후 시스템을 실행 시키면 알아서 DB 테이블을 생성해준다. 그리고 DB 테이블에 저장된 데이터를 맵핑된 클래스의 객체 형태로 가졍올 수 있다.
JPA 생성시 3개의 어노테이션은 반드시 지정해야 한다
JPA 사용시 .sql 파일을 생성하여 시스템 실행시 .sql을 실행 시킬 수 있다. 이를 통해 기본 데이터를 추가 가능하다.
Repository를 사용하여 DB의 데이터 조회 API를 사용할 수 있다
우선 User 테이블을 관리하는 Repository 생성
Controller에서 Repository 사용
Repository를 사용함으로써 SQL 쿼리를 사용하여 조회하지 않고 Repository의 API를 사용하여 데이터를 조회할 수 있다. 만약 Post에 대한 테이블을 만들고 제어하고 싶다면 Post에 대한 클래스로 테이블을 만들고 Post를 제어할 수 있는 Repository를 만들면 된다.
JPA를 사용하여 테이블관 관계를 표시할 수 설정할 수 있다
사용자는 여러개의 게시물을 작성하거나 게시물을 하나도 작성하지 않을 수 있다. 이를 클래스와 어노테이션만으로 표현 가능하다.
초기 테스트 데이터를 다음과 같이 넣어준다
사용자의 게시물을 반환하는 API를 구현
사용자 조회시 사용자와 관련된 post 정보도 함께 전달된다
사용자의 post 정보를 반환하는 API
Leonard Richardson : REST API 설계함. python beautiful source 개발
LEVEL 0 : get, delete, do 같은것을 굳이 URI로 표현할 필요 없음. Not Good
LEVEL 1 : 모든 요청을 get,post로 표현하고 결과를 200, 에러로 표현한다. Not Good
LEVEL 2 : get, post, put, delete를 사용. CRUD와 같은 개념을 표현.
LEVEL 3 : 응답값으로 다음작업이 어떤것이 있는지 알려줌(HATEOAS). 응답값만로 서버에 다음으로 호출할 URI를 알 수 있음.
사용자 입장에서 고려
HTTP 장점을 최대한 활용
methods(GET, POST, PUT, DELETE)를 제대로 사용
결과를 적절한 Reponse Status로 반환
보안 정보는 URI에 노출하면 안됨
복수 형태로 표현하도록 노력
리소스를 표현하기 위해 동사 형태보다는 명사형을 사용
일관된 API 인터페이스 사용