Skip to content

Commit

Permalink
회원 가입 시 스페이스 자동 생성 기능 리팩토링 (Fastcampus-Final-Team3#229)
Browse files Browse the repository at this point in the history
* feat : 비동기 예외 처리 클래스 구현

- 기본적으로 @async 메서드에서 발생하는 예외는 호출자에게 전파가 되지 않음.
- 이는 @async 어노테이션이 붙은 메서드가 별도의 스레드에서 실행되므로 메인 스레드에서 캐치를 할 수 없기 때문
- 비동기 메서드에서 발생하는 예외를 처리하는 클래스인 AsyncExceptionHandler를 구현
- AsyncUncaughtExceptionHandler 인터페이스를 구현하였으며, handleUncaughtException 메서드를 통해 예외를 처리

* feat : 비동기 설정 클래스 추가

- 비동기 처리를 위한 설정 클래스인 AsyncConfig를 추가

- 'threadPoolTaskExecutor'라는 이름의 Executor 빈을 등록하는 메서드를 구현
- Executor는 비동기 작업을 처리할 때 사용될 스레드 풀을 설정하며, corePoolSize는 5, maxPoolSize는 30, queueCapacity는 50으로 설정

- AsyncConfigurerSupport의 getAsyncUncaughtExceptionHandler() 메서드를 오버라이드하여 비동기 메서드에서 발생하는 예외를 처리하는 클래스인 AsyncExceptionHandler를 반환하도록 함

* refactor : 스페이스 생성 로직을 SpaceService 클래스로 분리

- 유저의 개인 및 단체 스페이스를 초기화하고 저장하는 작업을 비동기적으로 처리

* style : 불필요한 import 제거

* feat : MemberSignupEvent 클래스 생성 (Fastcampus-Final-Team3#228)

- Event를 사용해 이벤트로 사용하는 클래스라는 것을 명시적으로 표현
- 이벤트에 필요한 정보를 담고 있으며, 이벤트가 발생했을 때 해당 정보를 사용

* refactor : MemberSignupEvent 클래스 필드 제거 (Fastcampus-Final-Team3#228)

* refactor : 회원 가입 시 이벤트 디스패처를 통한 스페이스 초기화 로직 적용 (Fastcampus-Final-Team3#228)

* feat : 회원 가입 이벤트 핸들러 구현 (Fastcampus-Final-Team3#228)

* refactor : 비동기 처리 로직 제거 및 코드 정리 (Fastcampus-Final-Team3#228)

* refactor : TransactionalEventListener로 변경 (Fastcampus-Final-Team3#228)

- 트랜잭션이 성공할 때만 이벤트 핸들러를 실행되도록 TransactionalEventListener 사용

* refactor : TransactionalEventListener로 변경 (Fastcampus-Final-Team3#228)

- 스프링 트랜잭션 성공에 따라 이벤트 핸들러 실행되도록 변경

* style : 불필요한 공백 제거 (Fastcampus-Final-Team3#228)
  • Loading branch information
dpdmstjs authored Nov 12, 2023
1 parent cd0e9ab commit 1b6a5e2
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 50 deletions.
33 changes: 33 additions & 0 deletions src/main/java/com/javajober/core/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.javajober.core.config;

import java.util.concurrent.Executor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.javajober.core.exception.AsyncExceptionHandler;

@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {

@Bean(name="threadPoolTaskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(30);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("ASYNC-");
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.javajober.core.exception;

import java.lang.reflect.Method;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
log.info("Exception message - " + throwable.getMessage());
log.info("Method name - " + method.getName());
for (Object param: obj) {
log.info("Parameter value - " + param);
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/javajober/member/event/MemberSignupEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.javajober.member.event;

import com.javajober.member.domain.Member;

import lombok.Getter;

@Getter
public class MemberSignupEvent {

private final Member member;

public MemberSignupEvent(final Member member) {
this.member = member;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.javajober.member.event;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

import com.javajober.space.service.SpaceService;

@Component
public class MemberSignupEventHandler {

private final SpaceService spaceService;

public MemberSignupEventHandler(final SpaceService spaceService) {
this.spaceService = spaceService;
}

@Async("threadPoolTaskExecutor")
@TransactionalEventListener(classes = MemberSignupEvent.class, phase = TransactionPhase.AFTER_COMMIT)
public void handleMemberSignupEvent(final MemberSignupEvent event) {
spaceService.initializeAndSaveNewMemberSpaces(event.getMember());
}
}
58 changes: 15 additions & 43 deletions src/main/java/com/javajober/member/service/MemberService.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.javajober.member.service;


import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import javax.transaction.Transactional;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

Expand All @@ -17,34 +15,36 @@
import com.javajober.member.dto.MemberLoginResponse;
import com.javajober.member.dto.MemberSignupRequest;
import com.javajober.member.dto.MemberSignupResponse;
import com.javajober.member.event.MemberSignupEvent;
import com.javajober.member.repository.MemberRepository;
import com.javajober.core.refreshToken.repository.RefreshTokenRepository;
import com.javajober.core.security.JwtTokenizer;
import com.javajober.core.refreshToken.domain.RefreshToken;
import com.javajober.space.domain.AddSpace;
import com.javajober.space.domain.SpaceType;
import com.javajober.space.dto.request.SpaceSaveRequest;
import com.javajober.space.repository.AddSpaceRepository;

@Service
public class MemberService {
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
private final JwtTokenizer jwtTokenizer;
private final RefreshTokenRepository refreshTokenRepository;
private final AddSpaceRepository addSpaceRepository;

public MemberService(MemberRepository memberRepository, PasswordEncoder passwordEncoder, JwtTokenizer jwtTokenizer,
RefreshTokenRepository refreshTokenRepository, AddSpaceRepository addSpaceRepository) {
private final ApplicationEventPublisher eventPublisher;

public MemberService(
final MemberRepository memberRepository,
final PasswordEncoder passwordEncoder,
final JwtTokenizer jwtTokenizer,
final RefreshTokenRepository refreshTokenRepository,
final ApplicationEventPublisher eventPublisher
) {
this.memberRepository = memberRepository;
this.passwordEncoder = passwordEncoder;
this.jwtTokenizer = jwtTokenizer;
this.refreshTokenRepository = refreshTokenRepository;
this.addSpaceRepository = addSpaceRepository;
this.eventPublisher = eventPublisher;
}

@Transactional
public MemberSignupResponse signup(MemberSignupRequest memberSignupRequest) {
public MemberSignupResponse signup(final MemberSignupRequest memberSignupRequest) {
Optional<Member> existingMember = memberRepository.findMember(memberSignupRequest.getEmail());

if (existingMember.isPresent()) {
Expand All @@ -55,41 +55,13 @@ public MemberSignupResponse signup(MemberSignupRequest memberSignupRequest) {
member.setPassword(passwordEncoder.encode(memberSignupRequest.getPassword()));
Member saveMember = memberRepository.save(member);

initializeAndSaveNewMemberSpaces(member);
eventPublisher.publishEvent(new MemberSignupEvent(member));

return new MemberSignupResponse(saveMember);
}

private void initializeAndSaveNewMemberSpaces(Member member) {

SpaceSaveRequest personalSpaceRequest = createSpaceSaveRequest(member.getMemberName(), SpaceType.PERSONAL.getEngTitle(), member.getMemberName());
SpaceSaveRequest organizationSpaceRequest = createSpaceSaveRequest(member.getMemberName(), SpaceType.ORGANIZATION.getEngTitle(), "임시회사명");

Set<AddSpace> spaces = new HashSet<>();

AddSpace personalSpace = SpaceSaveRequest.toEntity(personalSpaceRequest, member);
spaces.add(personalSpace);

AddSpace organizationSpace = SpaceSaveRequest.toEntity(organizationSpaceRequest, member);
spaces.add(organizationSpace);

saveSpaces(spaces);
}

private SpaceSaveRequest createSpaceSaveRequest(String spaceTitle, String spaceType, String representativeName) {
return SpaceSaveRequest.builder()
.spaceTitle(spaceTitle)
.spaceType(spaceType)
.representativeName(representativeName)
.build();
}

private void saveSpaces(Set<AddSpace> spaces) {
addSpaceRepository.saveAll(spaces);
}

@Transactional
public MemberLoginResponse login(MemberLoginRequest loginDto) {
public MemberLoginResponse login(final MemberLoginRequest loginDto) {
Member member = memberRepository.findMember(loginDto.getEmail()).orElseThrow(()
-> new ApplicationException(ApiStatus.NOT_FOUND, "존재하지 않는 회원 아이디입니다."));

Expand Down
59 changes: 52 additions & 7 deletions src/main/java/com/javajober/space/service/SpaceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@
import com.javajober.space.dto.response.MemberGroupResponse;
import com.javajober.spaceWall.domain.FlagType;
import com.javajober.spaceWall.repository.SpaceWallRepository;

import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import javax.transaction.Transactional;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class SpaceService {

Expand All @@ -28,24 +36,62 @@ public class SpaceService {
private final AddSpaceRepository addSpaceRepository;
private final SpaceWallRepository spaceWallRepository;

public SpaceService(final MemberRepository memberRepository, final MemberGroupRepository memberGroupRepository,
final AddSpaceRepository addSpaceRepository, final SpaceWallRepository spaceWallRepository) {

public SpaceService(
final MemberRepository memberRepository,
final MemberGroupRepository memberGroupRepository,
final AddSpaceRepository addSpaceRepository,
final SpaceWallRepository spaceWallRepository
) {
this.memberRepository = memberRepository;
this.memberGroupRepository = memberGroupRepository;
this.addSpaceRepository = addSpaceRepository;
this.spaceWallRepository = spaceWallRepository;
}

public SpaceSaveResponse save(SpaceSaveRequest request, Long memberId) {

public void initializeAndSaveNewMemberSpaces(final Member member) {

log.info("initializeAndSaveNewMemberSpaces 시작: {}, 스레드 이름: {}", member.getMemberName(), Thread.currentThread().getName());

SpaceSaveRequest personalSpaceRequest = createSpaceSaveRequest(member.getMemberName(), SpaceType.PERSONAL.getEngTitle(), member.getMemberName());
SpaceSaveRequest organizationSpaceRequest = createSpaceSaveRequest(member.getMemberName(), SpaceType.ORGANIZATION.getEngTitle(), "임시회사명");

Set<AddSpace> spaces = new HashSet<>();

AddSpace personalSpace = SpaceSaveRequest.toEntity(personalSpaceRequest, member);
spaces.add(personalSpace);

AddSpace organizationSpace = SpaceSaveRequest.toEntity(organizationSpaceRequest, member);
spaces.add(organizationSpace);

saveSpaces(spaces);

log.info("initializeAndSaveNewMemberSpaces 종료: {}, 스레드 이름: {}", member.getMemberName(), Thread.currentThread().getName());
}

private SpaceSaveRequest createSpaceSaveRequest(final String spaceTitle, final String spaceType, final String representativeName) {
return SpaceSaveRequest.builder()
.spaceTitle(spaceTitle)
.spaceType(spaceType)
.representativeName(representativeName)
.build();
}

@Transactional
public void saveSpaces(final Set<AddSpace> spaces) {
addSpaceRepository.saveAll(spaces);
}


public SpaceSaveResponse save(final SpaceSaveRequest request, final Long memberId) {
Member member = memberRepository.findMember(memberId);
AddSpace space = SpaceSaveRequest.toEntity(request, member);
Long spaceId = addSpaceRepository.save(space).getId();

return new SpaceSaveResponse(spaceId);
}

public SpaceResponse find(Long addSpaceId, String spaceTypeString, Long memberId) {
public SpaceResponse find(final Long addSpaceId, final String spaceTypeString, final Long memberId) {
SpaceType spaceType = SpaceType.findSpaceTypeByString(spaceTypeString);
List<Long> addSpaceIds = addSpaceRepository.findAddSpaceIds(spaceType, memberId);
existsAddSpace(addSpaceId, addSpaceIds);
Expand All @@ -58,11 +104,10 @@ public SpaceResponse find(Long addSpaceId, String spaceTypeString, Long memberId
.orElseGet(() -> new SpaceResponse(false, 0L, memberGroupResponses));
}

private static void existsAddSpace(Long addSpaceId, List<Long> addSpaceIds) {
private static void existsAddSpace(final Long addSpaceId, final List<Long> addSpaceIds) {
boolean containsId = addSpaceIds.stream().anyMatch(id -> Objects.equals(id, addSpaceId));
if(!containsId) {
throw new ApplicationException(ApiStatus.NOT_FOUND, "존재하지 않는 스페이스입니다.");
}
}

}

0 comments on commit 1b6a5e2

Please sign in to comment.