Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b52501c
[FEAT] ProfileImage.isDefaultImage() 메서드 추가 (#99)
kmw2378 Feb 9, 2025
92f9d42
[FEAT] ProfileImageMoveEvent 추가 (#99)
kmw2378 Feb 9, 2025
69a08e2
[FEAT] ProfileImageEventListener 추가 (#99)
kmw2378 Feb 9, 2025
c454220
[REFACTOR] 프로필 이미지 처리 로직을 Spring Event Listener 를 사용해 처리하도록 변경 (#99)
kmw2378 Feb 9, 2025
d8b8215
[REFACTOR] StorageService 삭제 (#99)
kmw2378 Feb 9, 2025
4094089
[TEST] AuthService, MemberService 프로필 이미지 핸들링 테스트 수정 (#99)
kmw2378 Feb 9, 2025
34b1ec3
[REFACTOR] VerificationCode 패키지 변경 (#102)
kmw2378 Feb 9, 2025
e9a2ebb
[REFACTOR] VerificationCodeProvider 패키지 변경 (#102)
kmw2378 Feb 9, 2025
73a863b
[REFACTOR] VerificationCodeRepository 패키지 변경 (#102)
kmw2378 Feb 9, 2025
8f6f3ba
[REFACTOR] MailException 삭제 (#102)
kmw2378 Feb 9, 2025
0e6b8f9
[REFACTOR] MailTemplateProvider 삭제 (#102)
kmw2378 Feb 9, 2025
3e1e23c
[REFACTOR] MailService 삭제 (#102)
kmw2378 Feb 9, 2025
ecc9d64
[REFACTOR] MailConfig 삭제 (#102)
kmw2378 Feb 9, 2025
3c395ff
[FEAT] VerificationCodeService 추가 (#102)
kmw2378 Feb 9, 2025
0d81f6b
[FEAT] VerificationCodeMailRequest 추가 (#102)
kmw2378 Feb 9, 2025
13c3506
[FEAT] NotFoundVerificationCodeException 추가 (#102)
kmw2378 Feb 9, 2025
62a5739
[FEAT] MemberEmailVerificationEventListener 추가 (#102)
kmw2378 Feb 9, 2025
69c1d64
[FEAT] EmailVerificationCodeSendEvent 추가 (#102)
kmw2378 Feb 9, 2025
20e8185
[FEAT] MailProducer 추가 (#102)
kmw2378 Feb 9, 2025
1290053
[FEAT] 인증 코드 메일 메시지큐 설정 추가 (#102)
kmw2378 Feb 9, 2025
04b8061
[FEAT] 인증 코드 전송, 이메일 수정 로직 변경 (#102)
kmw2378 Feb 9, 2025
2eaf163
[CHORE] Thymeleaf, JavaMailSender 의존성 제거 (#102)
kmw2378 Feb 9, 2025
f298992
[TEST] MemberServiceTest 수정 (#102)
kmw2378 Feb 9, 2025
faac413
[FIX] 링크가 안나오는 에러 해결(#41)
SSUHYUNKIM Feb 9, 2025
5c6399e
Merge pull request #32 from JECT-Study/J01-99-BE-회원가입-프로필-이미지-핸들링-로직-수정
kmw2378 Feb 10, 2025
4c62821
Merge branch 'develop' into J01-102-BE-인증-코드-메일-전송-로직-수정
kmw2378 Feb 10, 2025
9302ae2
Merge pull request #33 from JECT-Study/J01-102-BE-인증-코드-메일-전송-로직-수정
kmw2378 Feb 10, 2025
80f9235
Merge pull request #34 from JECT-Study/J01-41-be-디자인-시스템-기능
kmw2378 Feb 10, 2025
c7ac001
Merge branch 'develop' of https://github.com/JECT-Study/Componote-BE …
kmw2378 Feb 10, 2025
bf684cb
[HOTFIX] MemberServiceTest 충돌 해결
kmw2378 Feb 10, 2025
15788ba
[REFACTOR] 기존 ComponentSearchRequest 클래스명 변경 (#105)
kmw2378 Feb 10, 2025
75f83cb
[REFACTOR] 새로운 ComponentSearchRequest 추가 (#105)
kmw2378 Feb 10, 2025
789d348
[FEAT] ComponentSearchResponse 추가 (#105)
kmw2378 Feb 10, 2025
adf318a
[FEAT] ComponentBookmarkRepository.findAllComponentIdsByMemberIdAndCo…
kmw2378 Feb 10, 2025
05152a1
[REFACTOR] 컴포넌트 검색 SQL 변경 (#105)
kmw2378 Feb 10, 2025
fbc6a5d
[FEAT] ComponentMapper.mapToSearchResponse() 추가 (#105)
kmw2378 Feb 10, 2025
c7706ff
[FEAT] ComponentBookmarkRepository에 ComponentBookmarkQueryDsl 상속 관계 추…
kmw2378 Feb 10, 2025
9c80950
[REFACTOR] ComponentService 검색 로직 수정 (#105)
kmw2378 Feb 10, 2025
c77e09e
[FEAT] 컴포넌트 검색 API 엔드포인트 추가 (#105)
kmw2378 Feb 10, 2025
15bb495
[REFACTOR] 컴포넌트 페이지 API 응답 DTO의 정적 팩토리 메서드 변경 (#105)
kmw2378 Feb 10, 2025
9c039a5
[REFACTOR] 더 이상 사용되지 않는 ComponentSummaryDao, ComponentDaoFactory 제거 (…
kmw2378 Feb 10, 2025
758ca06
[TEST] ComponentMapperTest에 mapToSearchResponse 테스트 코드 추가 (#105)
kmw2378 Feb 10, 2025
57d54b4
[TEST] ComponentServiceTest 변경 (#105)
kmw2378 Feb 10, 2025
4476218
Merge pull request #35 from JECT-Study/J01-105-BE-컴포넌트-네비게이션-바-API-추가
kmw2378 Feb 10, 2025
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
6 changes: 0 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

// Thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

// Mail
implementation 'org.springframework.boot:spring-boot-starter-mail'

// Bean Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ject.componote.domain.auth.dao.MemberRepository;
import ject.componote.domain.auth.dao.SocialAccountRepository;
import ject.componote.domain.auth.domain.Member;
import ject.componote.domain.auth.dto.image.event.ProfileImageMoveEvent;
import ject.componote.domain.auth.dto.login.request.MemberLoginRequest;
import ject.componote.domain.auth.dto.login.response.MemberLoginResponse;
import ject.componote.domain.auth.dto.signup.request.MemberSignupRequest;
Expand All @@ -15,16 +16,16 @@
import ject.componote.domain.auth.model.AuthPrincipal;
import ject.componote.domain.auth.model.Nickname;
import ject.componote.domain.auth.token.application.TokenService;
import ject.componote.infra.storage.application.StorageService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class AuthService {
private final StorageService storageService;
private final ApplicationEventPublisher eventPublisher;
private final MemberRepository memberRepository;
private final SocialAccountRepository socialAccountRepository;
private final TokenService tokenService;
Expand All @@ -41,7 +42,7 @@ public MemberSignupResponse signup(final MemberSignupRequest request) {
}

final Member member = memberRepository.save(request.toMember(socialAccountId));
storageService.moveImage(member.getProfileImage());
eventPublisher.publishEvent(ProfileImageMoveEvent.from(member));
final String accessToken = createAccessToken(member);
return MemberSignupResponse.of(accessToken, member);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ject.componote.domain.auth.application;

import ject.componote.domain.auth.dao.VerificationCodeRepository;
import ject.componote.domain.auth.domain.VerificationCode;
import ject.componote.domain.auth.dto.verify.event.EmailVerificationCodeSendEvent;
import ject.componote.domain.auth.util.VerificationCodeProvider;
import ject.componote.infra.mail.application.MailProducer;
import ject.componote.infra.mail.dto.VerificationCodeMailRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class MemberEmailVerificationEventListener {
private final MailProducer mailProducer;
private final VerificationCodeProvider verificationCodeProvider;
private final VerificationCodeRepository verificationCodeRepository;

@Async
@EventListener
public void handleVerificationCodeSendEvent(final EmailVerificationCodeSendEvent event) {
final String email = event.email();
if (email == null || email.isBlank()) {
return;
}
final VerificationCode verificationCode = verificationCodeProvider.createVerificationCode();
verificationCodeRepository.save(email, verificationCode);
mailProducer.sendVerificationMail(VerificationCodeMailRequest.of(email, verificationCode));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import ject.componote.domain.auth.dao.MemberRepository;
import ject.componote.domain.auth.domain.Member;
import ject.componote.domain.auth.dto.find.response.MemberSummaryResponse;
import ject.componote.domain.auth.dto.image.event.ProfileImageMoveEvent;
import ject.componote.domain.auth.dto.update.request.MemberEmailUpdateRequest;
import ject.componote.domain.auth.dto.update.request.MemberNicknameUpdateRequest;
import ject.componote.domain.auth.dto.update.request.MemberProfileImageUpdateRequest;
import ject.componote.domain.auth.dto.verify.event.EmailVerificationCodeSendEvent;
import ject.componote.domain.auth.dto.verify.request.MemberEmailVerificationRequest;
import ject.componote.domain.auth.error.DuplicatedEmailException;
import ject.componote.domain.auth.error.DuplicatedNicknameException;
Expand All @@ -15,19 +17,18 @@
import ject.componote.domain.auth.model.Email;
import ject.componote.domain.auth.model.Nickname;
import ject.componote.domain.auth.model.ProfileImage;
import ject.componote.infra.mail.application.MailService;
import ject.componote.infra.storage.application.StorageService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {
private final StorageService storageService;
private final MailService mailService;
private final ApplicationEventPublisher eventPublisher;
private final MemberRepository memberRepository;
private final VerificationCodeService verificationCodeService;

public MemberSummaryResponse getMemberSummary(final AuthPrincipal authPrincipal) {
final Long memberId = authPrincipal.id();
Expand All @@ -44,9 +45,8 @@ public void updateProfileImage(final AuthPrincipal authPrincipal, final MemberPr
return;
}

storageService.moveImage(profileImage);
member.updateProfileImage(profileImage);
memberRepository.save(member);
eventPublisher.publishEvent(ProfileImageMoveEvent.from(member));
}

@Transactional
Expand All @@ -62,18 +62,15 @@ public void updateNickname(final AuthPrincipal authPrincipal, final MemberNickna
}

member.updateNickname(nickname);
memberRepository.save(member);
}

@Transactional
public void updateEmail(final AuthPrincipal authPrincipal, final MemberEmailUpdateRequest request) {
final Email email = Email.from(request.email());
validateDuplicatedEmail(email);

final Member member = findMemberById(authPrincipal.id());
final Email email = Email.from(request.email());
validateSameEmail(member, email);

mailService.verifyEmailCode(request.email(), request.verificationCode());
validateDuplicatedEmail(email);
verificationCodeService.verifyEmailCode(request.email(), request.verificationCode());
member.updateEmail(email);
}

Expand All @@ -84,7 +81,7 @@ public void sendVerificationCode(final AuthPrincipal authPrincipal, final Member
final Member member = findMemberById(authPrincipal.id());
validateSameEmail(member, email);

mailService.sendVerificationCode(request.email());
eventPublisher.publishEvent(EmailVerificationCodeSendEvent.from(email));
}

private Member findMemberById(final Long memberId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ject.componote.domain.auth.application;

import ject.componote.domain.auth.dto.image.event.ProfileImageMoveEvent;
import ject.componote.domain.auth.model.ProfileImage;
import ject.componote.infra.storage.application.StorageProducer;
import ject.componote.infra.storage.dto.move.request.ImageMoveRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
@RequiredArgsConstructor
public class ProfileImageEventListener {
private final StorageProducer storageProducer;

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleImageMove(final ProfileImageMoveEvent event) {
final ProfileImage image = event.image();
if (image.isDefaultImage()) {
return;
}

storageProducer.sendImageMoveMessage(ImageMoveRequest.from(image));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ject.componote.domain.auth.application;

import ject.componote.domain.auth.dao.VerificationCodeRepository;
import ject.componote.domain.auth.domain.VerificationCode;
import ject.componote.domain.auth.error.NotFoundVerificationCodeException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class VerificationCodeService {
private final VerificationCodeRepository verificationCodeRepository;

public void verifyEmailCode(final String email, final String codeValue) {
final VerificationCode verificationCode = findVerificationCodeByEmail(email);
if (!verificationCode.equalsValue(codeValue)) {
throw new NotFoundVerificationCodeException();
}
verificationCodeRepository.deleteByEmail(email);
}

private VerificationCode findVerificationCodeByEmail(final String email) {
return verificationCodeRepository.findByEmail(email)
.orElseThrow(NotFoundVerificationCodeException::new);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ject.componote.infra.mail.repository;
package ject.componote.domain.auth.dao;

import ject.componote.infra.mail.model.VerificationCode;
import ject.componote.domain.auth.domain.VerificationCode;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package ject.componote.infra.mail.repository.impl;
package ject.componote.domain.auth.dao.impl;

import ject.componote.infra.mail.model.VerificationCode;
import ject.componote.infra.mail.repository.VerificationCodeRepository;
import ject.componote.domain.auth.dao.VerificationCodeRepository;
import ject.componote.domain.auth.domain.VerificationCode;
import org.springframework.stereotype.Repository;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

@Repository
public class InMemoryVerificationCodeRepository implements VerificationCodeRepository {
private final Map<String, VerificationCode> storage = new ConcurrentHashMap<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ject.componote.infra.mail.model;
package ject.componote.domain.auth.domain;

import java.time.LocalDateTime;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ject.componote.domain.auth.dto.image.event;

import ject.componote.domain.auth.domain.Member;
import ject.componote.domain.auth.model.ProfileImage;

public record ProfileImageMoveEvent(ProfileImage image) {
public static ProfileImageMoveEvent from(final Member member) {
return new ProfileImageMoveEvent(member.getProfileImage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ject.componote.domain.auth.dto.verify.event;

import ject.componote.domain.auth.model.Email;

public record EmailVerificationCodeSendEvent(String email) {
public static EmailVerificationCodeSendEvent from(final Email email) {
return new EmailVerificationCodeSendEvent(email.getValue());
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package ject.componote.infra.mail.error;
package ject.componote.domain.auth.error;

import org.springframework.http.HttpStatus;

public class NotFoundVerificationCodeException extends MailException {
public class NotFoundVerificationCodeException extends AuthException {
public NotFoundVerificationCodeException() {
super("인증 코드가 만료되었거나 잘못되었습니다.", HttpStatus.NOT_FOUND);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ public static ProfileImage from(final String objectKey) {

return new ProfileImage(objectKey);
}

public boolean isDefaultImage() {
return this == DEFAULT_PROFILE_IMAGE;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ject.componote.infra.mail.util;
package ject.componote.domain.auth.util;

import ject.componote.infra.mail.model.VerificationCode;
import ject.componote.domain.auth.domain.VerificationCode;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ject.componote.domain.bookmark.dao;

import java.util.Set;

public interface ComponentBookmarkQueryDsl {
Set<Long> findAllComponentIdsByMemberIdAndComponentIds(final Long memberId, final Set<Long> componentIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ject.componote.domain.bookmark.dao;

import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import static ject.componote.domain.bookmark.domain.QComponentBookmark.componentBookmark;

@RequiredArgsConstructor
public class ComponentBookmarkQueryDslImpl implements ComponentBookmarkQueryDsl {
private final JPAQueryFactory queryFactory;

@Override
public Set<Long> findAllComponentIdsByMemberIdAndComponentIds(final Long memberId, final Set<Long> componentIds) {
if (memberId == null || componentIds.isEmpty()) {
return Collections.emptySet();
}
return new HashSet<>(
queryFactory.select(componentBookmark.componentId)
.from(componentBookmark)
.where(componentBookmark.memberId.eq(memberId).and(componentBookmark.componentId.in(componentIds)))
.fetch()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import java.util.Optional;

@Repository
public interface ComponentBookmarkRepository extends JpaRepository<ComponentBookmark, Long> {
public interface ComponentBookmarkRepository extends JpaRepository<ComponentBookmark, Long>, ComponentBookmarkQueryDsl {
Page<ComponentBookmark> findAllByMemberId(Long memberId, Pageable pageable);
boolean existsByMemberIdAndComponentId(Long memberId, Long componentId);
Optional<ComponentBookmark> findByMemberIdAndComponentId(Long memberId, Long componentId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import ject.componote.domain.common.dto.response.PageResponse;
import ject.componote.domain.component.application.ComponentService;
import ject.componote.domain.component.dto.find.request.ComponentSearchRequest;
import ject.componote.domain.component.dto.find.request.ComponentSummaryRequest;
import ject.componote.domain.component.dto.find.response.ComponentSearchResponse;
import ject.componote.domain.component.dto.find.response.ComponentSummaryResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
Expand All @@ -23,14 +25,24 @@
public class ComponentController {
private final ComponentService componentService;

@GetMapping("/search")
public ResponseEntity<PageResponse<ComponentSummaryResponse>> search(
@GetMapping
public ResponseEntity<PageResponse<ComponentSummaryResponse>> getAllComponentSummaries(
@Authenticated final AuthPrincipal authPrincipal,
@ModelAttribute @Valid final ComponentSummaryRequest request,
@PageableDefault final Pageable pageable
) {
return ResponseEntity.ok(
componentService.getAllComponentSummaries(authPrincipal, request, pageable)
);
}

@GetMapping("/search")
public ResponseEntity<PageResponse<ComponentSearchResponse>> search(
@ModelAttribute @Valid final ComponentSearchRequest request,
@PageableDefault final Pageable pageable
) {
) {
return ResponseEntity.ok(
componentService.search(authPrincipal, request, pageable)
componentService.search(request, pageable)
);
}

Expand Down
Loading