일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- bfs dfs
- 백준 1992
- 비트마스킹
- 티스토리챌린지
- 노마드코더
- React Native
- 완전탐색
- 상속 관계 매핑
- 원복
- 폴더구조
- multipart upload
- react
- Navigation
- 버튼 활성화
- BFS
- 자료구조
- web view
- 해외 대외활동
- 휴대폰 기기
- service 테스트
- ReactNative
- FlatList
- springboot
- 구현
- React Natvive
- 오블완
- 이영직
- Project Bee
- 경우의 수
- 창의충전소
- Today
- Total
유미의 기록들
[개인과제 -Spring 숙련] 회원 CRUD API 구현 (Level 2) 본문
JPA를 활용하여 회원 CRUD API 구현하기
- 유저 저장, 단건 조회, 전체 조회, 삭제 기능
- 유저는 유저명, 이메일, 작성일, 수정일 필드를 가지고 있음
📝 개발 과정
1. schedules 데이터 베이스 생성
create database schedules;
use schedules;
2. JPA 환경 설정하기
build.gradle파일의 dependencies에 JPA 라이브러리를 설치한다
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
//JPA, 스프링 데이터 JPA 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
3. 연결 정보 설정
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/schedules
spring.datasource.username={연결 계정명}
spring.datasource.password={비밀번호}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
application.yml
spring:
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
format_sql: true
datasource:
url: jdbc:mysql://localhost:3306/schedules
username: {연결 계정명}
password: {비밀번호}
driver-class-name: com.mysql.cj.jdbc.Driver
프로그램을 실행했을 때 다음과 같은 로그가 나오면 연결 성공 !
먼저 회원 테이블을 생성하기 위해 ERD를 참고하여 Entity를 생성하였다
JPA에서 중요한 부분은 객체와 테이블을 매핑하는 것이다. Member객체와 테이블을 매핑하려고 한다
4. Entity 생성
@Entity
@Getter
@Setter
@Table(name="member")
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="user_name", nullable = false, length = 15)
private String userName;
@Column(length = 30)
private String email;
@Column(name="write_date", nullable = false)
private String writeDate;
@Column(name="update_date")
private String updateDate;
protected Member(){
}
public static Member createNewMember(String userName,String email,String writeDate){
Member newMember=new Member();
newMember.userName=userName;
newMember.email=email;
newMember.writeDate=writeDate;
return newMember;
}
}
`@Entity` : JPA가 사용하는 객체로 JPA에서 해당 클래스를 데이터베이스 테이블로 관리하는 엔티티로 인식한다
`@Id` : 고유한 식별값인 PK(Primary Key)와 해당 필드를 매핑한다
`@GerneratedValue(strategy = GenerationType.IDENTITY)` : PK 생성 값을 MySQL에서 auto increment 역할을 한다
`@Column` : 테이블의 컬럼과 객체의 필드를 매핑한다
- `name="user_name"` : 테이블의 컬럼명을 결정한다. (객체 필드의 카멜케이스를 테이블의 컬럼 _로 자동으로 변환해주기 때문에 이와 같은 경우에는 생략이 가능하다)
- `nullable = false` : NULL을 허용하지 않는다
- `length = 15`: 테이블의 컬럼 길이 값을 결정한다 (= VARCHAR 15)
JPA는 public또는 protected의 기본 생성자가 필수이다
protected Member(){}
외부에서 생성 부분의 코드를 통일시키기 위해 protected 접근제어자로 지정하였고, createNewMember함수를 통해 생성하도록 하였다
5. Controller
API명세서에 따라 클라이언트의 요청을 받아서 해당 메소드가 필요한 로직을 수행하도록 구현하였다
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
public class MemberController {
private final MemberService memberService;
//회원 저장
@PostMapping()
public CreateMemberResponse createMember(@RequestBody CreateMemberRequest request){
return memberService.createMember(request);
}
//회원 전체 조회
@GetMapping()
public MemberListResponse getMemberList(){
return memberService.getMemberList();
}
//회원 단건 조회
@GetMapping("/{id}")
public MemberResponse getMemberById(@PathVariable("id") Long id){
return memberService.getMemberById(id);
}
//회원 수정
@PutMapping("/{id}")
public UpdateMemberResponse updateMember(@PathVariable("id") Long id, @RequestBody UpdateMemberRequest request){
return memberService.updateMember(id,request);
}
//회원 삭제
@DeleteMapping("/{id}")
public DeleteMemberResponse deleteMember(@PathVariable("id") Long id){
return memberService.deleteMember(id);
}
}
CreateMemberRequest, CreateMemberResponse와 같은 DTO를 사용하여 클라이언트로 부터 요청을 받고 Service로 넘겨주도록 하였다 이렇게 하면 Entity와 API스펙을 명확히 분리할 수 있으며, 엔티티가 변해도 API스펙이 변하지 않게 된다
6. Service
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
public CreateMemberResponse createMember(CreateMemberRequest data){
//회원 객체 생성
Member newMember=Member.createNewMember(
data.getUserName(),
data.getEmail(),
data.getWriteDate()
);
//저장
Member savedMember=memberRepository.save(newMember);
return new CreateMemberResponse(
"회원 저장 성공",
201,
savedMember.getId()
);
}
public MemberListResponse getMemberList(){
//전체 조회
List<Member> foundMemberList=memberRepository.findAll();
//객체 -> DTO
List<MemberDto> memberDtoList=foundMemberList.stream()
.map(member -> new MemberDto(
member.getId(),
member.getUserName(),
member.getEmail(),
member.getWriteDate(),
member.getUpdateDate()
)).toList();
return new MemberListResponse(
"회원 전체 조회 성공",
200,
memberDtoList.size(),
memberDtoList
);
}
public MemberResponse getMemberById(Long id){
//단건 조회
Member foundMember=memberRepository.findById(id);
MemberDto memberDto=new MemberDto(
foundMember.getId(),
foundMember.getUserName(),
foundMember.getEmail(),
foundMember.getWriteDate(),
foundMember.getUpdateDate()
);
return new MemberResponse(
"회원 단건 조회 성공",
200,
memberDto
);
}
@Transactional
public UpdateMemberResponse updateMember(Long id,UpdateMemberRequest data){
//회원 조회
Member member=memberRepository.findOne(id);
log.info("member.getUserName={}",member.getUserName());
if(data.getUserName()!=null){
member.setUserName(data.getUserName());
}
if(data.getEmail()!=null){
member.setEmail(data.getEmail());
}
member.setUpdateDate(data.getUpdateDate());
Member updatedMember=memberRepository.findOne(id);
log.info("updatedMember.getUserName={}",updatedMember.getUserName());
return new UpdateMemberResponse(
"회원 수정 성공",
200,
updatedMember.getId()
);
}
@Transactional
public DeleteMemberResponse deleteMember(Long id){
//회원 조회
Member member=memberRepository.findOne(id);
if(member!=null){
memberRepository.deleteById(member);
}
return new DeleteMemberResponse(
"회원 삭제 성공",
200,
id
);
}
}
API명세서에 따라 응답값을 별도의 DTO를 사용하여 반환하도록 하였다
반환값으로는 message, statusCode, 데이터 값을 ResponseDTO에 담는다
7. Repository
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager entityManager;
//회원저장
@Transactional
public Member save(Member member){
entityManager.persist(member);
return member;
}
//회원 목록 조회
public List<Member> findAll(){
String jpql="SELECT m FROM Member m";
return entityManager
.createQuery(jpql,Member.class)
.getResultList();
}
//회원 단건 조회
public Member findById(Long id){
String jpql="SELECT m from Member m where m.id=:id";
return entityManager
.createQuery(jpql,Member.class)
.setParameter("id",id)
.getSingleResult();
}
public Member findOne(Long id){
return entityManager.find(Member.class,id);
}
public void deleteById(Member member){
entityManager.remove(member);
}
}
`private final EntityManager entityManager` : 스프링을 통해 `EntityManager`를 생성자 주입받는다. JPA는 기본적으로 한 요청 당, 하나의 EntityManager를 사용한다
`@Transcational` : JPA의 모든 데이터 변경(등록, 수정, 삭제)는 트랜잭션 안에서 수행되어야 한다.
저장
`entityManager.persist(member)` : JPA에서 객체를 테이블에 저장할 때는 `persist()`메소드를 사용한다
//JPA가 만들어서 실행한 SQL
insert into member (email, update_date, user_name, write_date) values (?, ?, ?, ?)
수정
`member.setUpdateDate(data.getUpdateDate())` : `entityManager.update()` 같은 메서드를 호출하지 않고, 엔티티 객체만 변경해도 UPDATE SQL이 실행된다.
*JPA는 트랜잭션이 커밋되는 시점에, 변경된 엔티티 객체가 있는 지 확인하고, 변경된 경우에는 UPDATE SQL을 실행한다. 이는 영속성 컨텍스트라는 JPA 내부원리를 이해해야 한다
//JPA가 만들어서 실행한 SQL
update member set email=?, update_date=?, user_name=?, write_date=? where id=?
조회
`entityManager.find(Member.class,id)` : 조회타입과 PK값을 주면 JPA에서 엔티티 객체를 PK 기준으로 조회한다. JPA가 조회SQL을 만들어서 실행하고, 결과값을 객체로 변환해서 준다
//JPA가 만들어서 실행한 SQL
select m1_0.id, m1_0.email, m1_0.update_date, m1_0.user_name, m1_0.write_date from member m1_0
JPQL (Java Persistence Query Language)
주로 여러 데이터를 복잡한 조건으로 조회할 때 사용한다. JPQL을 실행하면 그 안에 포함된 엔티티 객체의 매핑 정보를 활용해서 SQL을 만들게 된다
`entityManager.createQuery(jpql,Member.class)` : Member 엔티티 객체를 대상으로 "SELECT m FROM Member m"이라는 sql을 실행한다
* 여기서 동적쿼리를 생성하는데 큰 번거로움이 있는데, `Querydsl` 기술을 활용하면 깔끔하게 사용할 수 있다
💻 결과
회원 등록
회원 전체 조회
회원수정
회원 단건 조회
'대외활동 기록 > 내일배움캠프' 카테고리의 다른 글
[최종 프로젝트] Entity 계층 구조에 따른 상속 관계 매핑 (0) | 2024.11.07 |
---|---|
[개인과제 - Spring 플러스] 레거시 코드 리팩토링 (0) | 2024.10.11 |
[개인 과제 -Spring숙련] JPA를 활용한 일정관리 앱 서버 설계 (Level 1) (0) | 2024.08.22 |
[개인 과제 - Spring입문] 일정 관리 앱 서버 (0) | 2024.08.16 |
[팀 과제 - Java] 캠프 관리 프로그램 (0) | 2024.08.05 |