실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화 강의 - 인프런
스프링 부트와 JPA를 활용해서 API를 개발합니다. 그리고 JPA 극한의 성능 최적화 방법을 학습할 수 있습니다., 스프링 부트, 실무에서 잘 쓰고 싶다면? 복잡한 문제까지 해결하는 힘을 길러보세요
www.inflearn.com
해당 강의는 Inflearn에 등록된 김영한님의 Spring Boot 강의입니다.
API 및 기초는 다음 책을 참고 하였습니다.
출처:https://goldenrabbit.co.kr/product/springboot3java/
[되기] 스프링 부트 3 백엔드 개발자 되기(자바 편) - 골든래빗
자바 백엔드 개발자가 되고 싶다면 자바 그다음에 꼭 보세요! 입문자에게 백엔드 개발의 필수 지식을 학습 로드맵 중심으로 설명합니다. 스프링 부트 3 개발에 꼭 필요한 JPA ORM, OAuth2 인증, AWS 배
goldenrabbit.co.kr
이번 시간에는API 개발 기본에 대해 배워 보겠다. 그전에 만들었던 jpashop 파일과 DB를 그대로 사용하겠다.
0. API, REST API, HTTP 메서드
위의 예제는 클라이언트
,API
,서버
에 대한 설명을 그림으로 표현한 것이다.
API
웹사이트에서 예를 들어보자. 우리가 웹사이트 주소에 구글메인 화면을 보여달라 요청
해보자. 그러면 API는 이 요청을 받아서 서버에게 가져다 준다. 서버는 이 API가 준 요청을 처리해 결과물을 만들고 이것을 다시 API로 전달한다. 그러면 API는 최종 결과물을 브라우저
에게 보내고, 클라이언트는 화면을 볼 수 있게 된다.
REST API (REST=Representational State Transfer)
자원을 이름으로 구분해 자원의 상태를 주고받는 API 방식이다. _쉽게 말해 명확하고 이해하기 쉬운 API를 의미_한다.
즉, REST API는 URL의 설게 방식을 말한다.
(참고: 웹 개발이 초보이시분들이 흔히 착각하는게 REST API는 스프링부트,장고,Vue.js 같이 프레임워크 단위 기술이 아니다.)
사용 방법
규칙 1. URL에는 동사를 쓰지말고, 자원(명사)을 표시해야한다.
**<사용 예시>**
학생들 중 id가 1인 학생의 정보를 가져오는 url
- /students/1 -> 0
- /get-student?student_id=1 -> x
규칙 2. 동사는 HTTP 메서드로 표현한다.
**<사용 예시>
블로그에 글을 쓰는 설계를 한다고 가정하자.**
설명 | 적합한 HTTP 메서드 | url |
---|---|---|
id가 1인 블로그 글을 조회하는 API | GET | /articles/1 |
블로그 글을 추가하는 API | POST | /articles |
블로그 글을 수정하는 API | PUT | /articles/1 |
블로그 글을 삭제하는 API | DELETE | /articles/1 |
HTTP 메서드
다음과 같이 POST로 해당 url로 send(요청)해보자.
포스트맨에서 post로 해당 url로 요청을 하면 톰캣은 요청을 받고, 서버에서 처리할것이다.
즉, HTTP 메서드는 서버에 요청하는 방법을 나눈 것이다.
<종류 및 주 사용>
- POST: 만들기
- GET: 읽기
- PUT: 업데이트하기
- DELETE: 삭제하기
CRUD(create,read,update,delete)라고 한다.
(CRUD는 데이터베이스에도 사용되고, 거의 모든 소프트웨어 개발에서 데이터를 다룰 때 기본적으로 수행되는 작업이다.)
추가적으로 PATCH 메서드라는 것이 있다. PATCH는 리소스의 부분적인 업데이트를 위해 사용됩니다. 예를 들어, 사용자 프로필의 특정 정보만을 업데이트하고 싶을 때 유용하다.
이러한 HTTP 메서드를 적절히 사용하는 것은 RESTful API 디자인의 중요한 부분이다.
REST API 특징 및 장단점
특징: 서버/클라이언트 구조, 무상태,캐시 처리가능, 계층화, 인터페이스 일관성과 같은 특징이있 다.
장점: URL만 보고도 무슨 행동을 하는 API인지 명확하게알고, 상태가 없어 클라이언트와 서버의 역할이 명확하게 구분된다.
단점: HTTP 메서드의 개수에 제한이 있다.
REST하게 디자인한 API를 RESTful API라고 부르기도 한다.
1. DAO와 DTO
DAO (Data Access Object)
- 목적: DAO는 데이터베이스에 접근하는 객체이다. 데이터베이스의 CRUD(Create, Read, Update, Delete) 연산을 수행하는 메소드를 제공한다. 즉, 리포지토리를 의미한다.
- 사용 사례: 예를 들어, 사용자 정보를 관리하는 웹 애플리케이션에서 UserDAO 클래스는 사용자 데이터를 데이터베이스에 저장하고, 조회하고, 업데이트하고, 삭제하는 메소드를 제공할 수 있다.
DTO (Data Transfer Object)
- 목적: DTO는 계층 간 데이터를 전송하는 데 사용되는 객체이다. 주로 다른 네트워크 통신이나 메소드 호출을 통해 데이터를 전송할 때 사용되며, 여러 데이터 필드를 하나의 객체로 묶어 전달한다. DTO는 로직을 포함하지 않고 순수한 데이터 객체이며, 주로 getter와 setter 메소드를 포함한다.
- 사용 사례: 예를 들어, 웹 애플리케이션에서 클라이언트에게 사용자 정보를 전송할 때 UserDTO 객체를 사용할 수 있다. 이 객체는 사용자의 이름, 이메일 등의 정보를 포함할 수 있으며, 이를 통해 데이터를 쉽게 전송하고 받을 수 있다.
2. postman 설치
포스트맨 사이트 방문: https://www.getpostman.com
[Postman API Platform | Sign Up for Free
Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
www.postman.com](https://www.getpostman.com)
(포스트맨을 데스크톱에도 설치 하실 수 있습니다.)
포스트맨 설정
기본적인 포스트맨 구조(빨간표시를 잘 봐주시길 바랍니다.)
3. 회원 등록 API
API
// @Controller @ResponseBody 이 둘의 합은 => @RestController
@RestController //Rest API 스타일로 만드는 것(@ResponseBody: data를 json형태로 보냄)
@RequiredArgsConstructor
public class MemberApiController {
/**
* 회원 등록 api
*/
private final MemberService memberService;
//외부에 직접 노출
@PostMapping("/api/v1/members")
//@RequestBody: json으로 온 body를 멤버에에 그대로 매핑해서 넣어줌
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member)
{
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
//Dto로 노출
@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request)
{
Member member=new Member();
member.setName(request.name);
Long id=memberService.join(member);
return new CreateMemberResponse(id);
}
//DTO 생성
@Data
static class CreateMemberRequest
{
//controller에서 @valid Member member 실행시 NotEmpty하게
@NotEmpty
private String name;
}
@Data
static class CreateMemberResponse
{
private Long id;
//생성자
public CreateMemberResponse(Long id) {
this.id = id;
}
}
}
@RestController
설명
@RestController: Rest API 스타일로 만드는 것(@ResponseBody: data를 json형태로 보냄)
@Controller @ResponseBody 이 둘의 합은 => @RestController
- @Controller는 해당 클래스가 컨트롤러 역할을 한다는 것을 스프링에게 알려주며, 주로 뷰를 반환하는 데 사용된다.
- @ResponseBody는 컨트롤러의 메소드가 HTTP 응답 본문(Body)에 직접 데이터를 작성할 수 있도록 한다. 즉, 메소드의 반환값을 HTTP 응답 데이터로 사용하게 해준다.
@RestController 어노테이션을 사용하면, 해당 컨트롤러의 모든 메소드는 @ResponseBody를 암시적으로 사용하는 것으로 간주된다. 이는 메소드가 데이터를 직접 반환하며, 주로 JSON 형태로 변환되어 클라이언트에 전송된다는 의미입니다. 이를 통해 REST API를 쉽게 구현할 수 있다.
(단순 문자열 반환의 경우, 문자열 자체가 JSON으로 간주될 수 있지만, _객체 반환 시에는 JSON 객체로 자동 변환_됩니다).
@Data 어노테이션은 Lombok 라이브러리에서 제공하는 기능으로, 자바 클래스에 대한 게터(getter), 세터(setter), toString(), equals(), hashCode() 메소드를 자동으로 생성해주는 역할을한다.
CreateMemberRequest
를Member
엔티티 대신에RequestBody
와매핑
한다.- 엔티티와 프레젠테이션 계층을 위한 로직을 분리할 수 있다.
- 엔티티와 API 스펙을 명확하게 분리할 수 있다.
- 엔티티가 변해도 API 스펙이 변하지 않는다.
4. 회원 수정 API
updateMemberV2
추가
/**
* 회원 수정 api
*/
@PatchMapping("/api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2(
@PathVariable("id") Long id,
@RequestBody @Valid UpdateMemberRequest request)
{
memberService.update(id,request.getName()); //id로 조회하고, 이름을 수정
Member findMember=memberService.findOne(id); //수정한 행의 id를 불러옴
return new UpdateMemberResponse(findMember.getId(), findMember.getName());
}
//업데이트 용 request DTO
@Data
static class UpdateMemberRequest
{
private String name; // 이름 수정 요청
}
//업데이트용 응답 DTO
@Data
@AllArgsConstructor //롬복으로 둘의 값을 setting하는 생성자 생성
static class UpdateMemberResponse
{ private Long id; //아이디를 받아서
private String name;
}
MemberService
에 update 메서드 추가
/**
*회원 수정
*/
@Transactional
public void update(Long id, String name) {
Member member = memberRepository.findOne(id);
member.setName(name);
}
Patch
메서드로 사용자 정보 부분 수정하기.
(강의에서는 PUT으로 수정했으나, PUT은 전체 수정이므로 REST API 답게 PATCH 메서드 사용. )
포스트맨에서 jo->cho로 수정했는데 수정 성공했다. (변경감지를 통해 수정함)
5. 회원 조회 API
(회원 조회를 위해 먼저 스프링 부트 실행하고, 전에 만든 웹페이지 사이트에 접속하여 회원 목록들을 추가하시면 됩니다.)
<회원 조회V1: 엔티티를 직접 조회>
회원 조회 API V1
/**
회원 조회 API...V1: 엔티티를 직접 노출
*/
@GetMapping("api/v1/members")
public List<Member> membersV1()
{
return memberService.findAllmembers();
}
Member domain에서 주문
에 @JsonIgnore
를 넣으면 해당 데이터는 응답 데이터에서 빠짐.
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders=new ArrayList<>();
엔티티를 직접 외부에 노출하면 위험하다.(v1의 문제점)
<회원조회 V2: 응답 값으로 엔티티가 아닌 별도의 DTO 사용>
코드
/**
회원 조회 API...V2: 응답 값으로 엔티티가 아닌 별도의 DTO를 반환한다
*/
@GetMapping("api/v2/members")
public Result memberV2()
{
List<Member> findMembers=memberService.findAllmembers();
//엔티티 -> DTO 변환
//조회된 회원 목록을 스트림으로 변환한 후 map() 메서드를 사용하여 각 회원을 MemberDto 객체로 변환.
//변환된 객체들은 collect() 메서드를 사용하여 리스트로 수집함.
List<MemberDto> collect = findMembers.stream()
.map(m -> new MemberDto(m.getName()))
.collect(Collectors.toList());
return new Result(collect.size(),collect);
}
//GDTO
@Data
@AllArgsConstructor
// 요청 결과를 담는 클래스로, 조회된 회원 수와 데이터를 포함한다.
static class Result<T>
{
private int count;
private T data; // 향후 필요한 필드(데이터)를 추가할 수 있다.
}
@Data
@AllArgsConstructor
static class MemberDto //이름만 조회
{
private String name;
}
다음은 memberV2 메서드를 이해를 돕기 위해 for문으로 구현한것입니다.
@GetMapping("api/v2/members")
public Result memberV2() {
List<Member> findMembers = memberService.findAllmembers();
// 엔티티 -> DTO 변환
// 조회된 회원 목록을 for-loop를 사용하여 각 회원을 MemberDto 객체로 변환.
List<MemberDto> collect = new ArrayList<>();
for(Member member : findMembers) {
collect.add(new MemberDto(member.getName()));
}
return new Result(collect.size(), collect);
}
결과확인
위 코드를 통해 MemberDto에 이름만 조회하게 하게 하고,
T
는 제네릭 타입
이기 때문에, 어떤 형태의 데이터
든지 Result 클래스를 통해 전달할 수 있다.
(주문 API개발하면서 DTO에 추가적인 필드 삽입을 공부 할 예정입니다.
심지어 OrderItems와 같이 List로 여러개의 값을 가지는 필드도 DTO에 설정 하는 방법도 있습니다.)
6. @JsonIgnore 설정 안해줘서 생기는오류
`XToOne`관계는 `@JsonIgnore`를 설정 해줘야한다.
엔티티를 직접 노출할 때는 양방향 연관관계가 걸린 곳에 `@JsonIgnore `설정을 안해주면 안그러면 양쪽을 서로 호출하면서 무한 루프가 걸린다.