Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Spring Data JPA] 정다영 미션 제출합니다. #55

Open
wants to merge 13 commits into
base: day024
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 5 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,7 @@
# Spring MVC (인증)
# Spring Data JPA

<h3>1단계 로그인 요구사항</h3>
<h3>4단계 JPA 전환</h3>

- [x] 로그인 페이지 호출 (login.html 페이지 응답)
- [x] 로그인 요청(POST /login) 응답 API
- email과 password를 이용해서 멤버 조회/ 조회한 멤버로 토큰 생성/Cookie를 통한 응답
- [x] 인증 정보 조회(GET /login/check) API
- Cookie에서 토큰 정보 추출하여 멤버를 찾아 멤버 정보를 응답

<h3>2단계 로그인 리팩터링 요구사항</h3>

- [x] 멤버 객체를 만드는 로직을 분리(Cookie 인증 정보 활용)
- [x] 예약 생성 시 name이 없는 경우
- 예약 생성 시 ReservationReqeust의 name이 없는 경우 Cookie에 담긴 정보를 활용
- [x] Find Member 수정
- ReservationReqeust에 name값이 있으면 name으로 Member를 찾고 없으면 로그인 정보를 활용해서 Member를 찾는다.
- [x] HandlerMethodArgumentResolver 사용
- [x] 예약 생성
관리자는 name 인자로 전달한 정보로 예약을 생성하게 합니다. (기존 기능)
로그인 사용자는 자신의 로그인 정보로 예약을 생성하게 합니다. (신규 기능)

<h3>3단계 관리자 기능</h3>

- [ ] 어드민 페이지
- admin권한이 있는 사람만 할 수 있도록 제한
- [ ] HandlerInterceptor
- HandlerInterceptor를 활용하여 권한이 없는 경우 401코드 응답
- [ ] gradle 의존성 추가
- [ ] 엔티티 매핑
- [ ] 연관관계 매핑
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' //jpa변경

implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/roomescape/member/Member.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
package roomescape.member;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;
private String password;
private String role;

public Member(Long id, String name, String email, String role) {
public Member() {
}

public Member(Long id, String name, String email, String password, String role) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.role = role;
}

public Member(String name, String email, String password, String role) {
this.name = name;
this.email = email;
Expand Down
56 changes: 0 additions & 56 deletions src/main/java/roomescape/member/MemberDao.java

This file was deleted.

12 changes: 12 additions & 0 deletions src/main/java/roomescape/member/MemberRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package roomescape.member;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import roomescape.reservation.Reservation;

import java.util.List;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
Member findByEmailAndPassword(String email, String password);
}
36 changes: 21 additions & 15 deletions src/main/java/roomescape/member/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,45 @@
import org.springframework.stereotype.Service;
import roomescape.instructure.JwtTokenProvider;

import java.util.Optional;

@Service
public class MemberService {
@Autowired
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필드 주입과 생성자 주입이 혼용돼서 사용되고 있는데 생성자 주입으로 통일 시키는 것이 좋을 것 같습니다!

private MemberDao memberDao;
private MemberRepository memberRepository;
@Autowired
private JwtTokenProvider jwtTokenProvider;

public MemberService(MemberRepository memberRepository, JwtTokenProvider jwtTokenProvider) {
this.memberRepository = memberRepository;
this.jwtTokenProvider = jwtTokenProvider;
}

@Value("${roomescape.auth.jwt.secret}")
String secretKey;

public MemberResponse createMember(MemberRequest memberRequest) {
Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER"));
Member member = memberRepository.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER"));
return new MemberResponse(member.getId(), member.getName(), member.getEmail());
}


public MemberResponse findMember(String email, String password){
Member member = memberDao.findByEmailAndPassword(email,password);
return new MemberResponse(member.getId(),member.getName(),member.getEmail());
public MemberResponse findMember(String email, String password) {
Member member = memberRepository.findByEmailAndPassword(email, password);
return new MemberResponse(member.getId(), member.getName(), member.getEmail());
}

public MemberResponse findMemberById(Long memberId){
Member member = memberDao.findById(memberId);
return new MemberResponse(member.getId(),member.getName(),member.getEmail());
public MemberResponse findMemberById(Long memberId) {
Optional<Member> optionalMember = memberRepository.findById(memberId);
Member member = optionalMember.orElseThrow(() -> new IllegalArgumentException("Invalid member ID"));
return new MemberResponse(member.getId(), member.getName(), member.getEmail());
}

public String createToken(MemberResponse memberResponse){
public String createToken(MemberResponse memberResponse) {
String accessToken = jwtTokenProvider.createToken(memberResponse);
return accessToken;
}

public void createCookie(HttpServletResponse response, String token){
public void createCookie(HttpServletResponse response, String token) {
Cookie cookie = new Cookie("token", token);
cookie.setHttpOnly(true);
cookie.setPath("/");
Expand All @@ -51,12 +58,13 @@ public void createCookie(HttpServletResponse response, String token){
public Member extractMemberFromToken(String token) {
Long memberId = extractMemberIdFromToken(token);
if (memberId != null) {
return memberDao.findById(memberId);
Optional<Member> optionalMember = memberRepository.findById(memberId);
return optionalMember.orElse(null);
}
return null;
}

public Long extractMemberIdFromToken(String token){
public Long extractMemberIdFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes()))
.build()
Expand All @@ -71,8 +79,6 @@ public String extractTokenFromCookie(Cookie[] cookies) {
return cookie.getValue();
}
}

return "";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토큰을 찾지 못 했을 때 빈 문자열을 반환하는 것보다 null을 반환하는 것이 좋지 않을까요??

}

}
40 changes: 28 additions & 12 deletions src/main/java/roomescape/reservation/Reservation.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
package roomescape.reservation;

import jakarta.persistence.*;
import roomescape.member.Member;
import roomescape.theme.Theme;
import roomescape.time.Time;

@Entity
public class Reservation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String date;

@ManyToOne
@JoinColumn(name = "time_id")
private Time time;

@ManyToOne
@JoinColumn(name = "theme_id")
private Theme theme;

public Reservation(Long id, String name, String date, Time time, Theme theme) {
this.id = id;
this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
@ManyToOne // ManyToOne 관계 설정
@JoinColumn(name = "member_id")
private Member member;

public Reservation() {
}

public Reservation(String name, String date, Time time, Theme theme) {
public Reservation(String name, String date, Time time, Theme theme, Member member) {
this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
}

public Reservation() {

this.member = member;
}

public Long getId() {
Expand All @@ -48,4 +56,12 @@ public Time getTime() {
public Theme getTheme() {
return theme;
}
}

public Member getMember() {
return member; // Member 객체의 getter 메서드 추가
}

public void setMember(Member member) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엔티티에서는 setter 사용을 지양하는 것이 좋다고 합니다!
setter를 없애거나, 의도가 명확히 들어나는 메소드로 적는 것이 좋다고 해요..!
아래 블로그를 읽어 보시면 좋을 거 같습니다!
https://velog.io/@langoustine/setter-지양-이유

this.member = member; // Member 객체의 setter 메서드 추가
}
}
Loading