Skip to content

Commit

Permalink
feat: 사용자의 세션 로그인 기능을 구현한다.
Browse files Browse the repository at this point in the history
- CSRF 공격 방어는 고려하지 않음
- Auction 도메인 쪽 테스트의 인덴트 정렬
- 로그인을 완료하면 식별자와 역할 정보를 세션에 담습니다.
  • Loading branch information
HiiWee committed Aug 12, 2024
1 parent e18be65 commit 90e12db
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@ public void chargePoint(long price) {
public boolean isBuyer() {
return role.equals(Role.BUYER);
}

public boolean confirmPassword(String password) {
return this.password.equals(password);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface MemberRepository {
Member save(Member member);

Optional<Member> findById(Long id);

Optional<Member> findBySignInId(String signInId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.wootecam.luckyvickyauction.core.member.dto;

import com.wootecam.luckyvickyauction.core.member.domain.Role;
import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.Objects;

public record SignInInfo(
Long id,
Role role
) {
private static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public SignInInfo {
validateNotNull(id, "로그인한 사용자의 식별자");
validateNotNull(role, "로그인한 사용자의 역할");
}

private void validateNotNull(Object value, String fieldName) {
if (Objects.isNull(value)) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.G000);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.wootecam.luckyvickyauction.core.member.dto;

import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.Objects;

public record SignInRequestInfo(
String signInId,
String password
) {

private static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public SignInRequestInfo {
validateNotNull(signInId, "로그인 ID");
validateNotNull(password, "로그인 패스워드");
}

private void validateNotNull(Object value, String fieldName) {
if (Objects.isNull(value)) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.G000);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import java.util.Objects;

public record SignUpInfo(
public record SignUpRequestInfo(
String signUpId,
String password,
String userRole
) {

private static final String ERROR_NULL_VALUE = "%s는 Null일 수 없습니다.";

public SignUpInfo {
public SignUpRequestInfo {
validateNotNull(signUpId, "회원가입 ID");
validateNotNull(password, "회원가입 패스워드");
validateNotNull(userRole, "사용자 역할");
}

private void validateNotNull(Object value, String fieldName) {
if (Objects.isNull(value)) {
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.A007);
throw new BadRequestException(String.format(ERROR_NULL_VALUE, fieldName), ErrorCode.G000);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.wootecam.luckyvickyauction.core.member.domain.Member;
import com.wootecam.luckyvickyauction.core.member.domain.MemberRepository;
import com.wootecam.luckyvickyauction.core.member.dto.SignUpInfo;
import com.wootecam.luckyvickyauction.core.member.dto.SignInInfo;
import com.wootecam.luckyvickyauction.core.member.dto.SignInRequestInfo;
import com.wootecam.luckyvickyauction.core.member.dto.SignUpRequestInfo;
import com.wootecam.luckyvickyauction.global.exception.BadRequestException;
import com.wootecam.luckyvickyauction.global.exception.ErrorCode;
import jakarta.servlet.http.HttpSession;
Expand All @@ -13,19 +15,32 @@ public class MemberService {

private final MemberRepository memberRepository;

public void signUp(SignUpInfo signUpInfo) {
if (memberRepository.isExist(signUpInfo.signUpId())) {
throw new BadRequestException("이미 존재하는 아이디입니다. input=" + signUpInfo.signUpId(), ErrorCode.M000);
public void signUp(SignUpRequestInfo signUpRequestInfo) {
if (memberRepository.isExist(signUpRequestInfo.signUpId())) {
throw new BadRequestException("이미 존재하는 아이디입니다. input=" + signUpRequestInfo.signUpId(), ErrorCode.M000);
}
Member member = Member.createMemberWithRole(
signUpInfo.signUpId(),
signUpInfo.password(),
signUpInfo.userRole()
signUpRequestInfo.signUpId(),
signUpRequestInfo.password(),
signUpRequestInfo.userRole()
);

memberRepository.save(member);
}

public void signIn(SignInRequestInfo signInRequestInfo, HttpSession session) {
Member member = memberRepository.findBySignInId(signInRequestInfo.signInId()).orElseThrow(
() -> new BadRequestException("아이디에 해당되는 사용자를 찾을 수 없습니다. signInId=" + signInRequestInfo.signInId(),
ErrorCode.M002));

if (!member.confirmPassword(signInRequestInfo.password())) {
throw new BadRequestException("패스워드가 일치하지 않습니다.", ErrorCode.M003);
}

SignInInfo signInInfo = new SignInInfo(member.getId(), member.getRole());
session.setAttribute("signInInfo", signInInfo);
}

public void signOut(HttpSession session) {
session.invalidate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ public enum ErrorCode {
M000("로그인(회원가입) 시, 이미 존재하는 회원 아이디로 로그인을 시도한 경우 예외가 발생합니다."),
M001("로그인(회원가입) 시, 사용자의 역할(구매자, 판매자)를 찾을 수 없는 경우 예외가 발생합니다."),
M002("사용자 조회 시, 사용자를 찾을 수 없는 경우 예외가 발생합니다."),
M003("로그인 시, 입력 패스워드와 실제 패스워드가 다른 경우 예외가 발생합니다."),

// Payment 관련 예외 코드
P000("입찰 시, 로그인한 사용자가 구매자가 아닌 경우 예외가 발생합니다."),
P001("입찰 시, 사용자의 포인트가 부족한 경우 예외가 발생합니다.");
P001("입찰 시, 사용자의 포인트가 부족한 경우 예외가 발생합니다."),

// Global 예외
G000("DTO 생성 시, 필드의 값이 NULL인 경우 예외가 발생합니다.");

private final String description;

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,60 +21,60 @@
class UpdateAuctionCommandTest {
static Stream<Arguments> dtoArguments() {
return Stream.of(
Arguments.of("경매 재고는 인당 구매수량보다 작을 수 없다.", ErrorCode.A000,
1L, 10000, 1, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("최대 구매 수량 제한은 0보다 커야한다.", ErrorCode.A003,
1L, 10000, 999999, 0,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("변동 시간 단위는 0보다 커야한다.", ErrorCode.A005,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(0L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("경매의 시작시간은 종료 시간보다 이전이어야한다.", ErrorCode.A006,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now(), ZonedDateTime.now().minusSeconds(1), ZonedDateTime.now()),
Arguments.of("상품 원가는 0보다 커야한다.", ErrorCode.A002,
1L, 0, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("경매 유형은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
null, Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("sellerId는 Null일 수 없다.", ErrorCode.A007,
null, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("변동 주기(variationDuration)는 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
null,
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("시작 시간(startedAt)은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
null, ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("종료 시간(finishedAt)은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), null, ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("요청 시간(requestTime)은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), null)
Arguments.of("경매 재고는 인당 구매수량보다 작을 수 없다.", ErrorCode.A000,
1L, 10000, 1, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("최대 구매 수량 제한은 0보다 커야한다.", ErrorCode.A003,
1L, 10000, 999999, 0,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("변동 시간 단위는 0보다 커야한다.", ErrorCode.A005,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(0L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("경매의 시작시간은 종료 시간보다 이전이어야한다.", ErrorCode.A006,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now(), ZonedDateTime.now().minusSeconds(1), ZonedDateTime.now()),
Arguments.of("상품 원가는 0보다 커야한다.", ErrorCode.A002,
1L, 0, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("경매 유형은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
null, Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("sellerId는 Null일 수 없다.", ErrorCode.A007,
null, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("변동 주기(variationDuration)는 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
null,
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("시작 시간(startedAt)은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
null, ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("종료 시간(finishedAt)은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), null, ZonedDateTime.now(), ZonedDateTime.now()),
Arguments.of("요청 시간(requestTime)은 Null일 수 없다.", ErrorCode.A007,
1L, 10000, 999999, 10,
new ConstantPricePolicy(1000),
Duration.ofMinutes(1L),
ZonedDateTime.now().minusHours(1L), ZonedDateTime.now(), null)
);
}

Expand All @@ -96,27 +96,27 @@ void success_case() {

// expect
assertThatNoException().isThrownBy(() -> new UpdateAuctionCommand(
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, ZonedDateTime.now()
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, ZonedDateTime.now()
));
}

@ParameterizedTest(name = "{0}")
@MethodSource("dtoArguments")
@DisplayName("경매 옵션 변경 요청이 잘못된 경우 예외가 발생한다.")
void validation_test(
String displayName, ErrorCode expectedErrorCode,
Long auctionId, int originPrice, int stock, int maximumPurchaseLimitCount,
PricePolicy pricePolicy, Duration varitationDuration,
ZonedDateTime startedAt, ZonedDateTime finishedAt, ZonedDateTime requestedAt
String displayName, ErrorCode expectedErrorCode,
Long auctionId, int originPrice, int stock, int maximumPurchaseLimitCount,
PricePolicy pricePolicy, Duration varitationDuration,
ZonedDateTime startedAt, ZonedDateTime finishedAt, ZonedDateTime requestedAt
) {
// expect
assertThatThrownBy(() -> new UpdateAuctionCommand(
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, requestedAt))
.isInstanceOf(BadRequestException.class)
.satisfies(exception -> {
assertThat(exception).hasFieldOrPropertyWithValue("errorCode", expectedErrorCode);
});
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, requestedAt))
.isInstanceOf(BadRequestException.class)
.satisfies(exception -> {
assertThat(exception).hasFieldOrPropertyWithValue("errorCode", expectedErrorCode);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,18 @@ void notFound_should_throw_exception() {
ZonedDateTime finishedAt = ZonedDateTime.of(2024, 8, 9, 1, 0, 0, 0, ZoneId.of("Asia/Seoul"));

final UpdateAuctionCommand updateAuctionCommand = new UpdateAuctionCommand(
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, ZonedDateTime.now()
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, ZonedDateTime.now()
);

// when
when(auctionRepository.findById(auctionId)).thenReturn(Optional.empty());

// then
assertThatThrownBy(() -> auctionService.changeOption(updateAuctionCommand))
.isInstanceOf(NotFoundException.class)
.satisfies(exception -> assertThat(exception).hasFieldOrPropertyWithValue("errorCode", ErrorCode.A011));
.isInstanceOf(NotFoundException.class)
.satisfies(exception -> assertThat(exception).hasFieldOrPropertyWithValue("errorCode",
ErrorCode.A011));
}

@Test
Expand All @@ -180,30 +181,31 @@ void when_change_auction_that_is_started() {
ZonedDateTime requestTime = ZonedDateTime.of(2024, 8, 9, 2, 0, 0, 0, ZoneId.of("Asia/Seoul"));

final UpdateAuctionCommand updateAuctionCommand = new UpdateAuctionCommand(
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, requestTime
auctionId, originPrice, stock, maximumPurchaseLimitCount, pricePolicy,
varitationDuration, startedAt, finishedAt, true, requestTime
);

Auction auction = Auction.builder()
.startedAt(startedAt)
.finishedAt(finishedAt)
.sellerId(1L)
.productName("Test Product")
.originPrice(10000)
.stock(999999)
.maximumPurchaseLimitCount(10)
.pricePolicy(new ConstantPricePolicy(1000))
.variationDuration(Duration.ofMinutes(1L))
.isShowStock(true)
.build();
.startedAt(startedAt)
.finishedAt(finishedAt)
.sellerId(1L)
.productName("Test Product")
.originPrice(10000)
.stock(999999)
.maximumPurchaseLimitCount(10)
.pricePolicy(new ConstantPricePolicy(1000))
.variationDuration(Duration.ofMinutes(1L))
.isShowStock(true)
.build();

// when
when(auctionRepository.findById(auctionId)).thenReturn(Optional.of(auction));

// then
assertThatThrownBy(() -> auctionService.changeOption(updateAuctionCommand))
.isInstanceOf(NotFoundException.class)
.satisfies(exception -> assertThat(exception).hasFieldOrPropertyWithValue("errorCode", ErrorCode.A012));
.isInstanceOf(NotFoundException.class)
.satisfies(exception -> assertThat(exception).hasFieldOrPropertyWithValue("errorCode",
ErrorCode.A012));
}
}
}
Loading

0 comments on commit 90e12db

Please sign in to comment.