Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0'

Expand Down
9 changes: 5 additions & 4 deletions src/main/java/roomescape/auth/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.springframework.stereotype.Service;
import roomescape.auth.dto.LoginResponse;
import roomescape.member.domain.Member;
import roomescape.member.dto.MemberAuthInfo;

@Service
public class AuthService {
Expand All @@ -13,11 +14,11 @@ public AuthService(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}

public LoginResponse createToken(Member member) {
public LoginResponse createToken(MemberAuthInfo memberAuthInfo) {
String accessToken = jwtTokenProvider.createToken(
member.getId(),
member.getName(),
member.getRole().name());
memberAuthInfo.memberId(),
memberAuthInfo.name(),
memberAuthInfo.stringRole());

return new LoginResponse(accessToken);
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/roomescape/member/MemberController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import roomescape.auth.dto.LoginRequest;
import roomescape.auth.dto.LoginResponse;
import roomescape.member.domain.Member;
import roomescape.member.dto.MemberAuthInfo;
import roomescape.member.dto.MemberRequest;
import roomescape.member.dto.MemberResponse;

Expand Down Expand Up @@ -44,8 +45,9 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) {
@PostMapping("/login")
public ResponseEntity login(@RequestBody LoginRequest loginRequest,
HttpServletResponse httpServletResponse) {
Member member = memberService.getMemberWithLoginRequest(loginRequest);
LoginResponse loginResponse = authService.createToken(member);
MemberAuthInfo memberAuthInfo = memberService.getMemberWithLoginRequest(
loginRequest);
LoginResponse loginResponse = authService.createToken(memberAuthInfo);

Cookie cookie = new Cookie("token", loginResponse.accessToken());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

쿠키에 관한 세부 로직은 별도의 객체에서 관리해도 좋을 것 같아요.

cookie.setHttpOnly(true);
Expand Down
148 changes: 74 additions & 74 deletions src/main/java/roomescape/member/MemberDao.java
Original file line number Diff line number Diff line change
@@ -1,74 +1,74 @@
package roomescape.member;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import roomescape.member.domain.Member;
import roomescape.member.domain.Role;

@Repository
public class MemberDao {

private JdbcTemplate jdbcTemplate;

public MemberDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public Member save(Member member) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
var ps = connection.prepareStatement(
"INSERT INTO member(name, email, password, role) VALUES (?, ?, ?, ?)",
new String[]{"id"});
ps.setString(1, member.getName());
ps.setString(2, member.getEmail());
ps.setString(3, member.getPassword());
ps.setString(4, member.getRole().name());
return ps;
}, keyHolder);

return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(),
Role.USER);
}

public Member findByEmailAndPassword(String email, String password) {
return jdbcTemplate.queryForObject(
"SELECT id, name, email, role FROM member WHERE email = ? AND password = ?",
(rs, rowNum) -> new Member(
rs.getLong("id"),
rs.getString("name"),
rs.getString("email"),
Role.valueOf(rs.getString("role"))
),
email, password
);
}

public Member findByName(String name) {
return jdbcTemplate.queryForObject(
"SELECT id, name, email, role FROM member WHERE name = ?",
(rs, rowNum) -> new Member(
rs.getLong("id"),
rs.getString("name"),
rs.getString("email"),
Role.valueOf(rs.getString("role"))
),
name
);
}

public Member findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT id, name, email, role FROM member WHERE id = ?",
(rs, rowNum) -> new Member(
rs.getLong("id"),
rs.getString("name"),
rs.getString("email"),
Role.valueOf(rs.getString("role"))
),
id
);
}
}
//package roomescape.member;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

혹시 추후에 다시 사용하는 코드인가요?? 주석을 달아서 의도를 표현해도 좋을 것 같고, 그런게 아니라면 삭제하는것도 좋아보입니다.

//
//import org.springframework.jdbc.core.JdbcTemplate;
//import org.springframework.jdbc.support.GeneratedKeyHolder;
//import org.springframework.jdbc.support.KeyHolder;
//import org.springframework.stereotype.Repository;
//import roomescape.member.domain.Member;
//import roomescape.member.domain.Role;
//
//@Repository
//public class MemberDao {
//
// private JdbcTemplate jdbcTemplate;
//
// public MemberDao(JdbcTemplate jdbcTemplate) {
// this.jdbcTemplate = jdbcTemplate;
// }
//
// public Member save(Member member) {
// KeyHolder keyHolder = new GeneratedKeyHolder();
// jdbcTemplate.update(connection -> {
// var ps = connection.prepareStatement(
// "INSERT INTO member(name, email, password, role) VALUES (?, ?, ?, ?)",
// new String[]{"id"});
// ps.setString(1, member.getName());
// ps.setString(2, member.getEmail());
// ps.setString(3, member.getPassword());
// ps.setString(4, member.getRole().name());
// return ps;
// }, keyHolder);
//
// return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(),
// Role.USER);
// }
//
// public Member findByEmailAndPassword(String email, String password) {
// return jdbcTemplate.queryForObject(
// "SELECT id, name, email, role FROM member WHERE email = ? AND password = ?",
// (rs, rowNum) -> new Member(
// rs.getLong("id"),
// rs.getString("name"),
// rs.getString("email"),
// Role.valueOf(rs.getString("role"))
// ),
// email, password
// );
// }
//
// public Member findByName(String name) {
// return jdbcTemplate.queryForObject(
// "SELECT id, name, email, role FROM member WHERE name = ?",
// (rs, rowNum) -> new Member(
// rs.getLong("id"),
// rs.getString("name"),
// rs.getString("email"),
// Role.valueOf(rs.getString("role"))
// ),
// name
// );
// }
//
// public Member findById(Long id) {
// return jdbcTemplate.queryForObject(
// "SELECT id, name, email, role FROM member WHERE id = ?",
// (rs, rowNum) -> new Member(
// rs.getLong("id"),
// rs.getString("name"),
// rs.getString("email"),
// Role.valueOf(rs.getString("role"))
// ),
// id
// );
// }
//}
32 changes: 20 additions & 12 deletions src/main/java/roomescape/member/MemberService.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
package roomescape.member;

import java.util.Optional;
import org.springframework.stereotype.Service;
import roomescape.auth.dto.LoginRequest;
import roomescape.member.domain.Member;
import roomescape.member.domain.Role;
import roomescape.member.dto.MemberAuthInfo;
import roomescape.member.dto.MemberRequest;
import roomescape.member.dto.MemberResponse;
import roomescape.member.repository.MemberRepository;

@Service
public class MemberService {

private MemberDao memberDao;
private MemberRepository memberRepository;

public MemberService(MemberDao memberDao) {
this.memberDao = memberDao;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

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

public Member getMemberWithLoginRequest(LoginRequest loginRequest) {
return memberDao.findByEmailAndPassword(
public MemberAuthInfo getMemberWithLoginRequest(LoginRequest loginRequest) {
Optional<Member> memberOptional = memberRepository.findByEmailAndPassword(
loginRequest.email(),
loginRequest.password());
}
Member member = memberOptional.orElseThrow();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

예외에 대한 내용이 안 적혀있네요. 이 경우에는 어떤 예외가 던져지나요??


return new MemberAuthInfo(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

취향차이긴한데, MemberAuthInfo에서 정적 팩토리 메서드를 하나 만들어서 생성하는것도 좋아보이네요. 이 부분은 참고만 해주세요!

member.getId(),
member.getName(),
member.getRole().name()
);

public Member findById(Long id) {
return memberDao.findById(id);
}

public Member findByName(String name) {
return memberDao.findByName(name);
public Optional<Member> findByName(String name) {
return memberRepository.findByName(name);
}
}
30 changes: 30 additions & 0 deletions src/main/java/roomescape/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
package roomescape.member.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.util.HashSet;
import java.util.Set;
import roomescape.reservation.domain.Reservation;

@Entity
public class Member {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Comment on lines +17 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@GenerateValue 전략에는 어떤게 있나요? IDENTITY를 선택한 이유가 있나요??

private Long id;

private String name;

private String email;

private String password;

@Enumerated(EnumType.STRING)
private Role role;

@OneToMany(mappedBy = "member")
private Set<Reservation> reservations = new HashSet<>();

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 생성자는 어디에 필요한거에요??

this.id = id;
this.name = name;
Expand All @@ -22,6 +44,10 @@ public Member(String name, String email, String password, Role role) {
this.role = role;
}

public Member() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

public인 이유가 있을까요? protected 혹은 private은 불가능한가요?? private이 불가능하다면 왜 불가능할까요??


}

public Long getId() {
return id;
}
Expand All @@ -41,4 +67,8 @@ public String getPassword() {
public Role getRole() {
return role;
}

public Set<Reservation> getReservations() {
return reservations;
}
}
5 changes: 5 additions & 0 deletions src/main/java/roomescape/member/dto/MemberAuthInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package roomescape.member.dto;

public record MemberAuthInfo(Long memberId, String name, String stringRole) {

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

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import roomescape.member.domain.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

네이밍이 변경되었네요. DAO 대신에 Repository를 쓴 이유가 있을까요? 어떤 차이점을 느껴서 Repository로 네이밍을 변경하신거에요??


Optional<Member> findByName(String name);

Optional<Member> findByEmailAndPassword(String email, String password);
}
30 changes: 26 additions & 4 deletions src/main/java/roomescape/reservation/ReservationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
import roomescape.member.domain.Member;
import roomescape.member.domain.Role;
import roomescape.member.MemberService;
import roomescape.reservation.dto.MyReservationResponse;
import roomescape.reservation.dto.ReservationRequest;
import roomescape.reservation.dto.ReservationResponse;
import roomescape.reservation.dto.WaitingRequest;
import roomescape.reservation.dto.WaitingResponse;

@RestController
public class ReservationController {
Expand All @@ -35,6 +38,11 @@ public List<ReservationResponse> list() {
return reservationService.findAll();
}

@GetMapping("/reservations-mine")
public List<MyReservationResponse> getMyReservations(@LoginMember Member member) {
return reservationService.findByMember(member);
}

@PostMapping("/reservations")
public ResponseEntity create(@RequestBody @Valid ReservationRequest reservationRequest,
@LoginMember Member loginMember) {
Expand All @@ -45,10 +53,24 @@ public ResponseEntity create(@RequestBody @Valid ReservationRequest reservationR
.body(reservation);
}


@DeleteMapping("/reservations/{id}")
public ResponseEntity delete(@PathVariable Long id) {
reservationService.deleteById(id);
public ResponseEntity delete(@PathVariable Long id, @LoginMember Member member) {
reservationService.deleteById(id, member);
return ResponseEntity.noContent().build();
}

@PostMapping("/waitings")
public ResponseEntity createWaiting(@RequestBody @Valid WaitingRequest waitingRequest,
@LoginMember Member loginMember) {
WaitingResponse waiting = reservationService.saveWaiting(waitingRequest, loginMember);

return ResponseEntity.created(URI.create("/reservations/" + waiting.id()))
.body(waiting);
}

@DeleteMapping("/waitings/cancel/{id}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

어차피 HTTP Method가 delete인데, cancel이라는 꾸밈어가 필요한가요??

public ResponseEntity cancelWaiting(@PathVariable Long id, @LoginMember Member member) {
reservationService.deleteWaitingById(id, member);
return ResponseEntity.noContent().build();
}

Expand All @@ -57,7 +79,7 @@ private Member resolveReservationMember(ReservationRequest reservationRequest,
Member member;

if (loginMember.getRole() == Role.ADMIN && reservationRequest.name() != null) {
member = memberService.findByName(reservationRequest.name());
member = memberService.findByName(reservationRequest.name()).orElseThrow();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

기존에 있던 코드 같긴 한데, 별도의 Interceptor + ArgumentResolver를 사용하면, 더 응집도있게 권한 검증을 할 수 있을 것 같아요.

return member;
}
return loginMember;
Expand Down
Loading