그러면 Service Provider에서는 Resource Owner가 로그인하여 리소스 사용을 승인할 수 있는 페이지로 302 응답을 통해 Resource Owner를 이동시킨다.
이 페이지는 Client가 사용할 권한 목록을Resource Owner에게 명시적으로 보여주며, Resource Owner는 이에 동의 시 "나는 Client가 Scope에 명시한 범위 안에 있는 내 데이터에 접근할 권한을 부여하는 것에 동의합니다"라고 말하는 것과 같다.
3. Access Token 발행 요청
Client는 서비스 제공자에게 Client Id와 Secret Key, Access Token이 발행되면 아까 Authentication Code를 발급 받을때 사용했던 Redirect Url, 그리고 아까 Resource Owner에게 받은 인가 코드를 가지고 Access Token을 발행 요청을 한다.
서비스 제공자의 인증 서버는 리소스에 접근할 수 있는 Access Token과 Refresh Token을 발행하고 Client에게 보내준다.
4. 리소스에 접근 요청
Access Token을 받고 Client는 이 토큰을 통해 Resource Server에서 사용자의 정보에 접근한다.
Access Token이 저장된 DB에 비교하여 지정된 Scope에 접근하는 게 맞는지 확인 후 돌려준다.
보통 헤더에 많이 세팅한다.
5. Access Token 재발급 요청
시간이 지나서 Access Token이 만료된다면 좀 전에 말했듯이 Refresh Token을 통해 새로 발급받을 수 있다.
Access Token을 발급받을 때 보통 expires라는 항목으로 유효시간이 같이 넘어오는데, 이를 통해 유효시간이 지났는지 확인할 수 있고 지났으면 Refresh Token으로 재발급받는다.
🍀 구글 OAuth API 프로젝트 환경구성
구글 API를 사용하기 위해서는 우선 하단의 사이트에서 일련의 구성및 허가 과정을 거쳐야 한다.
Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있다.
전통적인 Spring MVC의 컨트롤러인 @Controller와 Restuful 웹서비스의 컨트롤러인 @RestController의 주요한 차이점은 HTTP Response Body가 생성되는 방식이다.
근본적인 차이는 @Controller의 역할은 Model 객체를 만들어 데이터를 담고 View를 찾는 것이지만, @RestController는 단순히 객체만을 반환하고 객체 데이터는 JSON 또는 XML 형식으로 HTTP 응답에 담아서 전송한다.
@Controller은 뷰에 표시될 데이터가 있는 Model 객체를 만들고 올바른 뷰를 선택하는 일을 담당한다.
또한, @ResponseBody를 사용하여HTTP Response Body에 데이터를 담아 요청을 완료할 수 있다.
HTTP Response Body에 데이터를 담는 것은 RESTful 웹 서비스에 대한 응답에 매우 유용한데, 뷰를 반환하는 대신 데이터를 반환하기 때문이다.
하지만 @RestController을 사용하여 동일한 기능을 제공할 수 있다.
요컨대 @Controller와 @ResponsBody의 동작을 하나로 결합한 편의 컨트롤러라 보면 된다.
다음 두 코드는 Spring MVC에서 동일한 동작을 한다.
@Controller
@ResponseBody
public class MVCController{
logic...
}
@RestController
public class ReftFulController{
logic...
}
대부분의 개발자들은 두개의 어노테이션이 아닌 하나의 어노테이션만 선언하고 싶어할 것이다.
또한, @RestController는 이전 두개보다 의미에 대해서 명확히 나타내고 있다.
@RestController vs @Controller
1. @Controller는 클래스를 Spring MVC 컨트롤러로 표시하는데 사용되며 @RestController는 RESTful 웹 서비스에서 사용되는 특수 컨트롤러이다. @Controller + @ResponseBody와 동일하다.
** RESTful 웹서비스란 : url을 통해 데이터를 요청하고 그 데이터는 XML 형식으로 반환
출처 : https://kimseunghyun76.tistory.com/18
+) REST 의 특징
1) REST 방식의 웹서비스는 잘 정의된 Cool URI로 리소스를 표현.
무분별한 파라미터의 남발이 아닌 마치 오브젝트의 멤버 변수를 따라가듯이 정의
2) REST 방식의 웹서비스는 세션을 쓰지 않는다.
기존의 서블릿 개발에서는 세션을 이용해서 인증 정보를 가지고 다니는데 요청 처리가 매우 무거워진다. 그리고 요청의 전후 관계에 관련성이 생기기에 한 세션의 일련의 요청을 하나의 서버가 처리해야 한다. 하지만 REST는 세션을 사용하지 않기에 각각의 요청이 완벽하게 독립적이다.
3) REST란 4가지 속성을 지향하는 웹서비스 디자인 표준이다.
ROA는 웹의 모든 리소스를 URI로 표현하고, 모든 리소스를 구조적이고 유기적으로 연결하여 비 상태 지향적인 방법으로 정해진 method만을 사용하는 아키텍쳐다.
* Addressablilty (주소로 표현 가능함) - 제공하는 모든 정보를 URI로 표시할 수 있어야 한다.
* Connectedness (연결됨) - 일반 웹 페이지처럼 하나의 리소스들은 서로 주변의 연관 리소스들과 연결되어 표현(Presentation)되어야 한다.
* Statelessness (상태 없음) - 현재 클라이언트의 상태를 절대로 서버에서 관리하지 않아야 한다.(REST에서는 상태가 서버가 아니라 클라이언트에 유지되며 매 요청마다 필요한 정보를 서버에 보낸다.) - 모든 요청은 일회성의 성격을 가지며 이전의 요청에 영향을 받지 말아야 한다. - 세션을 유지 하지 않기 때문에 서버 로드 밸런싱이 매우 유리하다. - URI에 현재 state를 표현할 수 있어야 한다. (권장사항)
* Homogeneous Interface (동일한 인터페이스) - HTTP에서 제공하는 기본적인 4가지의 method와 추가적인 2가지의 method를 이용해서 리소스의 모든 동작을 정의한다. - 대부분의 리소스 조작은 6가지의 method를 이용하여 대부분 처리 가능하다. 만일 이것들로만 절대로 불가능한 액션이 필요할 경우에는 POST를 이용하여 추가 액션을 정의할 수 있다. (되도록 지양하자)
REST는 웹의 모든 리소스를 URI로 표현하고 이를 구조적이고 유기적으로 연결하여 비 상태 지향적인 방법으로 일관된 method를 사용하여 리소스를 사용하는 웹 서비스 디자인 표준이다.
2. @RestController는 Spring 4.0에서 추가되었지만, @Controller는 Spring이 주석을 지원하기 시작한 이후에 존재하며 공식적으로 Spring 2.5 버전에서 추가되었다.
3. @Controller는 @Component 주석이 달려있고 @RestController는 아래와 같이 @Controller와 @ResponseBody 주석이 달린 편의 컨트롤러이다.
@Target(value=TYEP)
@Retention(value=RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController
@Target(value=TYEP)
@Retention(value=RUNTIME)
@Documented
@Component
public @interface Controller
4. 주요 차이점 중 하나는 @RestController을 표시하면 모든 메소드가 뷰 대신 객체로 작성된다!
일반적인 Spring MVC 처리 과정
위 사진에서 볼 수 있다싶이 @RestController를 클래스에 달면 모든 핸들러 메소드에서 @ResponseBody를 사용할 필요가 없다!
@Controller
@RequestMapping("books")
public class SimpleBookController {
@GetMapping("/{id}", produces = "application/json")
public @ResponseBody Book getBook(@PathVariable int id) {
return findBookById(id);
}
private Book findBookById(int id) {
// ...
}
}
@RestController
@RequestMapping("books-rest")
public class SimpleBookRestController {
@GetMapping("/{id}", produces = "application/json")
public Book getBook(@PathVariable int id) {
return findBookById(id);
}
private Book findBookById(int id) {
// ...
}
}
결론은 REStful 웹 서비스를 만드는 경우 @Controller + @ResponseBody를 사용하는 것보다 @RestController을 사용하는 것이 좋다!
@Entity
// name과 age Column에 unique 제약조건 추가
@Table(name="MEMBER", uniqueConstraints = {@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"} )})
public class Member {
// 기본키 매핑
@Id
@Column(name = "ID")
private String id;
// not null, varchar(10)
@Column(name = "NAME", nullable = false, length = 10)
private String username;
private Integer age;
// Java의 enum 사용
@Enumerated(EnumType.STRING)
private RoleType roleType;
// Java의 날짜 타입
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
// CLOB, BLOC 타입 매핑
@Lob
private String description;
@Transient
private String temp;
//Getter, Setter
}
public enum RoleType {
ADMIN, USER
}
4. 데이터베이스 스키마 자동 생성
- JPA는 데이터베이스 스키마를 자동으로 생성하는 기능 지원
- 클래스의 매핑 정보와 데이터베이스 방언을 사용하여 데이터베이스 스키마 생성
- 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성
hibernate.hbm2ddl.auto 속성
create : 기존 테이블을 삭제하고 새로 생성(DROP + CREATE)
create-drop : CREATE 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거 (DROP + CREATE + DROP)
update: DB 테이블과 엔티티 매핑 정보를 비교해서 변경 사항만 수정
validate : DB 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않음.
DDL을 수행하지 않음
none : 자동 생성 기능을 사용하지 않음
주의사항
- 개발 초기 단계는 create 또는 update
- 초기화 상태로 자동화된 테스트를 진행하는 개발자 환경과 CI서버는 create 또는 create-drop
- 테스트 서버는 update 또는 validate
- 스테이징과 운영 서버는 validate 또는 none
5. 기본 키 매핑
- 영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들기 위해 식별자 값 반드시 필요
@GeneratedValue
- 기본 키 생성 전략
* 직접 할당: 기본 키를 애플리케이션에 직접 할당
=> em.persist()를 호출하기 전 애플리케이션에서 직접 식별자 값을 할당해야 함. 식별자 값이 없을 경우 예러 발생
* 자동 생성: 대리 키 사용 방식
1) IDENTITY: 기본 키 생성을 데이터 베이스에 위임 (= AUTO_INCREMENT)
2) SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당, 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장함. 유일한 값을 순서대로 생성 (오라클, PostgreSQL, DB2, H2) 3)TABLE: 키 생성 테이블을 사용키 생성 전용 테이블 하나를 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어
데이터베이스 시퀀스를 흉내내는 전략. 테이블을 사용하므로 모든 데이터베이스에 적용 가능
4) AUTO: 선택한 데이터베이스 방언에 따라 방식을 자동으로 선택(Default)
Ex) 오라클 DB 선택 시 SEQUENCE, MySQL DB 선택 시 IDENTITY 사용
@Column
- 객체 필드를 테이블 컬럼에 매핑
- 속성 중 name, nullable이 주로 사용되고 나머지는 잘 사용되지 않음
1) name : 필드와 매핑할 테이블 컬럼 이름 (default. 객체의 필드 이름)
2) nullable (DDL) : null 값의 허용 여부 설정, false 설정 시 not null (default. true) @Column 사용 시 nullable = false 로 설정하는 것이 안전
3) unique (DDL) : @Table 의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 적용
4) columnDefinition (DDL) : 데이터베이스 컬럼 정보를 직접 줄 수 있음, default 값 설정 (default. 필드의 자바 타입과 방언 정보를 사용해 적절한 컬럼 타입을 생성)
5) length (DDL) : 문자 길이 제약조건, String 타입에만 사용 (default. 255)
6) percision, scale (DDL) : BigDecimal, BigInteger 타입에서 사용. 아주 큰 숫자나 정밀한 소수를 다룰 때만 사용(default. precision = 19, scale = 2)
@Enumerated
- 자바의 enum 타입을 매핑할 때 사용
* value : EnumType.ORDINAL : enum 순서를 데이터베이스에 저장
EnumType.STRING : enum 이름을 데이터베이스에 저장 (default.EnumType.ORDINAL)
@Temporal
- 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용
* value : TemporalType.DATE : 날짜, 데이터베이스 data 타입과 매핑 (2020-12-18)
TemporalType.TIME : 시간, 데이터베이스 time 타입과 매핑 (23:36:33)
TemporalType.TIMESTAMP : 날짜와 시간, 데이터베이스 timestamp 타입과 매핑 (2020-12-18 23:36:33)
(default. TemporalType은 필수로 지정)
@Temporal 을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의
@Lob
- 데이터베이스 BLOB, CLOB 타입과 매핑
* 지정 속성이 없음. 대신 매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB로 매핑
@Transient
- 이 필드는 매핑하지 않음. 데이터베이스에 저장하지 않고 조회하지도 않음.
객체에 임시로 어떤 값을 보관하고 싶을 때 사용
@Access
- JPA가 엔티티 데이터에 접근하는 방식 지정
* 필드 접근 : AccessType.FIELD로 지정, 필드에 직접 접근 (private도 접근 가능)
* 프로퍼티 접근 : AccessType.PROPERTY로 지정 (접근자 Getter 사용)
5. 엔티티 설계시 주의점
- 엔티티에는 가급적 Setter 사용 X (Setter가 모두 열려있다 == 변경 포인트가 너무 많아서 유지 보수 어렵다)
- 모든 연관 관계는 지연로딩으로 설정! (개중요!!!!!!!! 실제 프로젝트 쓸 때 오류 해결할 때 유용)
* 즉시로딩(EAGER = 멤버를 조회할 때 연관된 Order 조회를 다 하는 것)는 예측 어렵고 어떤 SQL이 실행될지 추적하기 어렵
* 실무에서 모든 연관관계는 지연로딩(LAZY)로 설정
* 문맥상에서 원하는 것만 선택해 가져올 수 있음
* 연관된 엔티티를 함께 DB에서 조회해야 하면 fetch, join 또는 엔티티 그래프 기능을 사용한다
* @XToOne 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 함
- 컬렉션은 필드에서 초기화하자
* 컬렉션은 필드에서 초기화하는 것이 안전
* null 문제에서 안전
- 테이블, 컬럼명 생성 전략 (SpringPhysicalNamingStrategy)
* 스프링부트 신규 설정 (엔티티(필드) -> 테이블(컬럼))
> 카멜 케이스 => 언더스코어 (memberPoint -> member_point)
> .(점) => _(언더스코어)
> 대문자 => 소문자
* 논리명? => 명시적으로 컬럼, 테이블명을 직접 적지 않으면 ImplicitNamingStrategy 사용