

프로젝트 생성 뒤, build.gradle에서 위처럼 의존성이 잘 추가되었는지 확인
인텔리제이 오른편에 위치하는 DB에서 MySQL을 선택해 Data Source를 생성하고(todoPlus) schema를 만들어줍니다
이렇게하면 초기 설정 완료!
JPA는 코드를 통해 DB의 테이블을 만들고 내부의 객체를 다룰 수 있다.
1. 일정 테이블 요구사항
작성 유저명, 할일 제목, 할일 내용, 작성일&수정일(JPA Auditing 활용)
2. 회원 테이블 요구사항
유저명, 비밀번호, 이메일, 작성일 & 수정일(JPA Auditing 활용)
Application(main) 클래스에 반드시 @EnableJpaAuditing 이 어노테이션을 추가해야만 작성 수정일을 자동으로 만들어준다!!!
두 테이블 모두 공통적으로 작성일과 수정일이 자동으로 생성 및 수정되어야하므로 하나의 클래스에서 두 객체를 다루고, 이를 상속해 사용할 수 있도록 하자.
@Getter //가져다쓸수있게
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime modifiedAt;
}
@MappedSuperclass
: 공통 필드(createdAt, modifiedAt) 를 여러 엔티티에서 공유하면서,
실제 테이블은 생성되지 않도록 하는 역할
@EntityListeners(AuditingEntityListener.class)
:JPA에게 너 자동으로 일 좀 해라ㅋ라고 하는 것.
JPA Auditing 기능을 활성화
→ @CreatedDate, @LastModifiedDate 같은 자동 시간 등록 기능이 동작
이렇게하면 생성 수정일을 JPA가 알아서 관리해준다
아까 만든 BaseEntity를 상속해 만든다
public class Member extends BaseEntity {
JPA에게 이 클래스를 테이블로 사용해 라고 하려면
@Getter //가져가서 쓰렴
@Entity //이거 엔티티임
@Table(name = "member") //테이블명 지정해주기
@Table 어노테이션에 이름을 지정해서 클래스에 붙여주면 된다.
회원 관리 클래스에 필요한 객체는 유저명, 비밀번호, 이메일
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String name;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
@GeneratedValue(strategy = GenerationType.IDENTITY)
: 데이터베이스가 자동으로 ID 값을 생성 (AUTO_INCREMENT 기능 사용)
@Column
: 객체의 제한사항 적용하기
→ null이 가능한지, 중복값을 가질 수 있는지 등..
그리고 나중에 사용할 때는 이름, 비번, 이메일만 필요할 때가 있으므로
아래처럼 만들어준다
public Member(String name, String password, String email){
this.name = name;
this.password = password;
this.email = email;
}
public Member() {}//기본 생성자
Member Entity와 비슷하게 만들면 된다.
작성 유저명, 할일 제목, 할일 내용을 만들어보자
@Getter
@Entity
@Table(name = "schedule")
public class Todo extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Setter
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@Column(nullable = false)
private String title;
@Column(columnDefinition = "longtext")
private String content;
여기서 중요한 점은 바로 일정의 작성자를 Member 테이블에서 가져와서 사용한다는 것이다.
한명의 회원은 여러개의 게시글을 작성할 수 있으므로 N:1 단방향 관계
→ @ManyToOne
@JoinColumn(name = "member_id")을 사용해서
Member테이블에서 같은 이름을 찾아 Join하고, 일정테이블에서는 작성 유저명 필드 대신 유저 고유 식별자 필드(id)를 사용한다
이렇게 클래스를 만든 후, 다이어그램을 보면

todo테이블의 member_id가 외래키로 member테이블의 기본키인 id를 가르키고 있는 모습을 확인할 수 있다.
회원을 생성해보자
회원가입할때는
이름 name, 비번 password, 이메일 email을 요청해야하고
응답할때는 이름 name, 이메일 email, 고유 식별번호 id를 반환해야한다.
@Getter
public class SignUpResponseDto {
private final Long id;
private final String name;
private final String email;
public SignUpResponseDto(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
JPA를 활용하면, 인터페이스를 통해 자동으로 데이터를 CRUD해준다
public interface MemberRepository extends JpaRepository<Member, Long> {}
JpaRepository<Member, Long> 인터페이스를 상속해 그 기능을 사용할 수 있도록 해주자
컨트롤러에서 이름, 비번, 이메일을 입력받아 Service의 signup 메서드에 보내주면, 서비스에서 이를 레포지토리에 넘겨 저장한 후, 고유 id값을 받아 다시 반환해주는 로직을 구현하자
@RestController
@RequestMapping("/members") //prefix
@RequiredArgsConstructor //생성자 알아서 만들기
public class MemberController {
private final MemberService memberService;
@PostMapping("/signup")
public ResponseEntity<SignUpResponseDto> signUp(@RequestBody SignUpRequestDto dto) {
//서비스 클래스에서 반환한 값을 signUpResponseDto에 담아 보내준다
SignUpResponseDto signUpResponseDto = memberService.signUp(dto.getName(), dto.getPassword(), dto.getEmail());
return new ResponseEntity<>(signUpResponseDto, HttpStatus.CREATED);
}
@Service
@AllArgsConstructor
public class MemberService {
private final MemberRepository memberRepository; //레포 가져오기
public SignUpResponseDto signUp(String name, String password, String email) {
Member member = new Member(name, password, email); //멤버 엔티티로 새로운 멤버 객체를 만들고
Member savedMember = memberRepository.save(member); //레포에 save해준다
return new SignUpResponseDto(savedMember.getId(), savedMember.getName(), savedMember.getEmail());
}; //id, 이름, 이메일을 reponse
}

성공 응답을 받는 모습을 확인할 수 있다!

DB에서도 자동으로 생성 수정일이 등록되어 저장된 모습을 확인할 수 있다
조회 기능을 구현해보자
전체적인 로직은 create와 비슷하지만 id를 활용해 유저를 찾을 때, repository에서 달라지는 부분이 존재한다
주의할점은 조회할때 비밀번호가 노출되면 안된다는것이다.
그러려면 멤버를 반환하는 DTO를 따로 만들어주어야한다.
1. MemberResponseDto
@Getter
public class MemberResponseDto {
private final Long id;
private final String name;
private final String email;
public MemberResponseDto(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
2. Controller - findAllMember()
여러 회원을 반환해야하므로 List<>
형태는 MemberResponseDto의 형식과 같게 반환해야한다
//모든 회원 조회하기
@GetMapping
public ResponseEntity<List<MemberResponseDto>> findAllMember() {
List<MemberResponseDto> members = memberService.findAllMember();
return new ResponseEntity<>(members, HttpStatus.OK);
}
이렇게 작성하고 서비스의 findAllMember()를 만들어보자
3. ⭐️ Service - findAllMember()
public List<MemberResponseDto> findAllMember() {
List<Member> members = memberRepository.findAll();
return members.stream().map(
member -> new MemberResponseDto(member.getId(), member.getName(), member.getEmail())).toList();
}
members리스트를 레포에서 받아오되, 우리는 비밀번호를 제외한 값들을 반환해주어야하므로 아까 만들어둔 DTO형식으로 반환해보자
stream을 사용할때 map()을 사용해서 내가 원하는 값만 뽑아 출력할 수 있따.
member -> new MemberResponseDto(member.getId(), member.getName(), member.getEmail())
map()안에 이렇게 ->를 통해 MemberResponseDto형식으로 담아 보내주면 된다!!!
그러면 포스트맨에서

이와 같이 MemberResponseDto 형식으로 반환된 모습을 볼 수 있다!!
1. Controller
id를 통해 조회해야하므로 @GetMapping할때 "/{id}"를 함께 적자
그리고 id를 매칭해야하므로 @PathVariable 어노테이션을 사용!!
전체 조회때와 같이 비밀번호를 노출하면 안되므로 MemberResponseDto로 반환할 수 있도록 한다
//회원 단건 조회 by ID
@GetMapping("/{id}")
public ResponseEntity<MemberResponseDto> findMemberById(@PathVariable Long id){
MemberResponseDto memberResponseDto = memberService.findMemberById(id);
return new ResponseEntity<>(memberResponseDto, HttpStatus.OK);
}
memberService.findMemberById(id); 를 통해 조회해보자
2. Service
//id로 단건조회
public MemberResponseDto findMemberById(Long id) {
Member member = memberRepository.findMemberByIdOrElseThrow(id);
return new MemberResponseDto(member.getId(), member.getName(), member.getEmail());
}
인자로 넘겨받은 id를 사용해 레포지토리(DB)에서 조회할 수 있도록 하자
3. Repository
JPA에서는 메서드 이름으로 DB를 활용할 수 있는데,
어떤 단어를 써야할지 고민될땐 꼭 공홈의 Query Method를 참고하자!!!
나는 id를 통해서 찾고, 예외처리도 해주고 싶으니까
findById & orElseThrow를 사용해서
"찾는다 / 멤버를 / 아이디 / 통해서 / 아니면 / 던져 에러" 이런식의 작명을 하면된다
(약간 웃김ㅋㅋㅋㅋ 나만?)
이를 활용해서 아까 만들어둔 레포 클래스에 적용하면 된다.
public interface MemberRepository extends JpaRepository<Member, Long> {
default Member findMemberByIdOrElseThrow(Long id) {
return findById(id).orElseThrow(()-> new ResponseStatusException(HttpStatus.NOT_FOUND, "입력하신 id의 회원은 존재하지 않습니다"));
}
}
에러 처리를 한 번에 해줄 수 있으니 참 편하다 크크
이제 유저 email과 비밀번호를 통해 유저의 정보를 수정할 수 있도록 해보자
→ 여기서 비번이 일치하지 않으면 실패 반환해야함!!
@RequestBody를 통해 MemberRequestDto를 입력받도록 하자(JSON 형태로 입력받을 수 있게 함) //회원 정보 삭제 by 이름
@DeleteMapping
public ResponseEntity<Boolean> deleteMemberById(@RequestBody MemberRequestDto dto){
}
boolean result = memberService.delete(dto.getEmail(), dto.getPassword());
if (result){
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
만약 result가 true라면 삭제하고, false면 BAD_REQUEST를 날리자
//service
//이메일을 통해 멤버 조회 후, 비밀번호가 맞으면 삭제 로직 수행
public boolean delete(String email, String password) {
String findPw = memberRepository.findMemberdByEmail(email).get().getPassword();
if (findPw.equals(password)){
memberRepository.delete(memberRepository.findMemberdByEmail(email).get());
return true;
} else {
return false;
}
}
memberRepository.findMemberdByEmail(email).get().getPassword(); 이 부분을 통해 레포에서 이메일에 맞는 비번을 찾아와야한다.
//이메일주소로 회원조회하기
Optional<Member> findMemberdByEmail(String email);
findMemberdByEmail을 통해 JPA가
"SELECT * FROM member WHERE email = "내가 넣은 이메일"
쿼리를 실행할 수 있도록 해주자
여기서 받은 회원 정보를 사용해서 service에서 findMemberdByEmail(email).get().getPassword();를 통해 비밀번호를 조회할 수 있는거다
=> 만약 내가 입력한 비번과 저장된 비번이 같으면 삭제하고, 아니면 실패 반환!

잘 삭제되는 것을 볼 수 있다
delete와 로직이 비슷하다
Controller
사용자에게 email oldPassword newPassword를 입력받아서 DB에 저장된 비번과 사용자가 입력한 기존비번이 같으면 새로운 비밀번호로 갈아주는 로직을 수행하자!!
//회원 비번 수정 by 이메일 & 비번
@PutMapping
public ResponseEntity<Boolean> updateMemberPassword(@RequestBody UpdatePasswordResponseDto dto){
boolean result = memberService.updateMemberPassword(dto.getEmail(), dto.getOldPassword(), dto.getNewPassword());
if (result){
return new ResponseEntity<>(HttpStatus.OK);
}else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
Boolean타입으로 성공했는지 아닌지 반환받도록 했다.
boolean result = memberService.updateMemberPassword(dto.getEmail(), dto.getOldPassword(), dto.getNewPassword());
service에서 비번 바꾸는 로직을 만들어 result에 반영해보자
Service
//이메일과 기존 비번 바꿀 비번을 입력해, 만약 기존 비번이 일치하면 비번 바꿔주기
public boolean updateMemberPassword(String email, String oldPassword,String newPassword) {
String findPw = memberRepository.findMemberdByEmail(email).get().getPassword();
Member member = memberRepository.findMemberdByEmail(email).get();
if (findPw.equals(oldPassword)){
member.updatePassword(newPassword);
memberRepository.save(member);
return true;
} else {
return false;
}
}
삭제 로직과 같이, 비밀번호를 비교하는 로직을 구현하되
멤버 정보를 가져와서 Member 엔티티에 새로운 비번을 할당하는 로직을 만들었다.
//Member 엔티티
public void updatePassword(String password){
this.password = password;
}
이 로직 수행 후 다시 DB에 저장해야하므로 save() 해주자
memberRepository.save(member);

이렇게 OK를 반환받는 것을 확인할 수 있고,

DB에도 잘 반영된 모습을 확인할 수 있따!!
이렇게 오늘 회원을 등록하고 조회하고 수정하고 삭제하는 로직을 구현했다!!
이제 다음시간에는 게시판에 글을 작성하되, 이 회원정보와 연동될 수 있게하는 기능을 구현해보자!