Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.example.RealMatch.user.application.service;

import java.util.Optional;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.RealMatch.global.exception.CustomException;
import com.example.RealMatch.user.domain.entity.NotificationSetting;
import com.example.RealMatch.user.domain.entity.Term;
import com.example.RealMatch.user.domain.entity.UserTerm;
import com.example.RealMatch.user.domain.entity.enums.TermName;
import com.example.RealMatch.user.domain.repository.NotificationSettingRepository;
import com.example.RealMatch.user.domain.repository.TermRepository;
import com.example.RealMatch.user.domain.repository.UserTermRepository;
import com.example.RealMatch.user.presentation.code.UserErrorCode;
import com.example.RealMatch.user.presentation.dto.request.NotificationSettingUpdateRequest;
import com.example.RealMatch.user.presentation.dto.response.NotificationSettingResponse;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional
public class NotificationSettingService {

private static final TermName MARKETING_TERM_NAME = TermName.MARKETING_CONSENT;

private final NotificationSettingRepository notificationSettingRepository;
private final UserTermRepository userTermRepository;
private final TermRepository termRepository;

/**
* 내 알림 설정 조회
*/
@Transactional(readOnly = true)
public NotificationSettingResponse getMySetting(Long userId) {

NotificationSetting setting = notificationSettingRepository
.findOneByUserId(userId)
.orElseThrow(() ->
new CustomException(UserErrorCode.USER_NOTIFICATION_SETTING_NOT_FOUND)
);

boolean marketingConsent = userTermRepository
.findByUserIdAndTermName(userId, MARKETING_TERM_NAME)
.isPresent(); // ⭐ 존재 여부로 판단

return NotificationSettingResponse.builder()
.marketingConsent(marketingConsent)
.appPushEnabled(setting.isAppPushEnabled())
.emailEnabled(setting.isEmailEnabled())
.build();
}

/**
* 내 알림 설정 수정 (설정 완료)
*/
public void updateSetting(Long userId, NotificationSettingUpdateRequest request) {

// 1️⃣ 알림 설정 업데이트
NotificationSetting setting = notificationSettingRepository
.findOneByUserId(userId)
.orElseThrow(() ->
new CustomException(UserErrorCode.USER_NOTIFICATION_SETTING_NOT_FOUND)
);

setting.update(
request.isAppPushEnabled(),
request.isEmailEnabled()
);

Optional<UserTerm> optionalUserTerm =
userTermRepository.findByUserIdAndTermName(userId, MARKETING_TERM_NAME);

boolean wantMarketingConsent = request.isMarketingConsent();

// Case 1: 동의 안 함 → 기존 row 있으면 삭제
if (!wantMarketingConsent) {
optionalUserTerm.ifPresent(userTermRepository::delete);
return;
}

// Case 2: 동의 함 → 기존 row 없으면 생성
if (optionalUserTerm.isEmpty()) {
Term marketingTerm = termRepository
.findByName(MARKETING_TERM_NAME)
.orElseThrow(() ->
new CustomException(UserErrorCode.INVALID_TERM)
);

UserTerm newUserTerm = UserTerm.builder()
.user(setting.getUser())
.term(marketingTerm)
.isAgreed(true)
.build();

userTermRepository.save(newUserTerm);
}
Comment on lines +76 to +99
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

마케팅 수신 동의 여부를 처리하는 로직이 return을 사용하여 두 부분으로 나뉘어 있어 흐름을 파악하기 다소 어렵습니다. if-else if 구조를 사용하여 동의/비동의 케이스를 명확하게 분리하면 가독성을 높일 수 있습니다.

        boolean wantMarketingConsent = request.isMarketingConsent();
        boolean hasMarketingConsent = optionalUserTerm.isPresent();

        // Case 1: 동의 함 → 기존 row 없으면 생성
        if (wantMarketingConsent && !hasMarketingConsent) {
            Term marketingTerm = termRepository
                    .findByName(MARKETING_TERM_NAME)
                    .orElseThrow(() ->
                            new CustomException(UserErrorCode.INVALID_TERM)
                    );

            UserTerm newUserTerm = UserTerm.builder()
                    .user(setting.getUser())
                    .term(marketingTerm)
                    .isAgreed(true)
                    .build();

            userTermRepository.save(newUserTerm);
        } else if (!wantMarketingConsent && hasMarketingConsent) { // Case 2: 동의 안 함 → 기존 row 있으면 삭제
            optionalUserTerm.ifPresent(userTermRepository::delete);
        }

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@

public enum NotificationChannel {
PUSH,
EMAIL,
SMS
EMAIL
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.example.RealMatch.user.domain.entity.enums;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum TermName {
AGE,
SERVICE_TERMS,
PRIVACY_COLLECTION,
PRIVACY_THIRD_PARTY,
MARKETING_CONSENT,
MARKETING_PRIVACY_COLLECTION,
MARKETING_NOTIFICATION

AGE("만 14세 이상입니다"),
SERVICE_TERMS("서비스 이용약관에 동의합니다"),
PRIVACY_COLLECTION("개인정보를 수집하고 이용하는 것에 동의합니다"),
PRIVACY_THIRD_PARTY("개인정보를 제3자에게 제공하는 것에 동의합니다"),
MARKETING_CONSENT("이벤트 혜택과 광고성 정보 수신에 동의합니다"),
MARKETING_PRIVACY_COLLECTION("마케팅 목적의 개인정보 수집과 이용에 동의합니다"),
MARKETING_NOTIFICATION("이메일과 앱 푸시 알림 수신에 동의합니다");

private final String displayName;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
public interface NotificationSettingRepository extends JpaRepository<NotificationSetting, Long> {

Optional<NotificationSetting> findByUserId(Long userId);
Optional<NotificationSetting> findOneByUserId(Long userId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

findOneByUserId 메서드는 이미 동일한 기능을 하는 findByUserId 메서드가 존재하므로 중복됩니다. user_id는 유니크 제약조건이 있으므로 findByUserIdOptional<NotificationSetting>을 반환하는 것은 올바른 시그니처입니다. 코드의 일관성을 유지하고 중복을 피하기 위해 새로 추가된 findOneByUserId 메서드를 삭제하고, 대신 findByUserId를 사용해주세요.


}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.RealMatch.user.domain.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

Expand All @@ -12,4 +13,6 @@ public interface TermRepository extends JpaRepository<Term, Long> {
List<Term> findByNameIn(List<TermName> names);

List<Term> findByIsRequired(boolean isRequired);

Optional<Term> findByName(TermName name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.jpa.repository.JpaRepository;

import com.example.RealMatch.user.domain.entity.UserTerm;
import com.example.RealMatch.user.domain.entity.enums.TermName;

public interface UserTermRepository extends JpaRepository<UserTerm, Long> {

Expand All @@ -14,4 +15,6 @@ public interface UserTermRepository extends JpaRepository<UserTerm, Long> {
Optional<UserTerm> findByUserIdAndTermId(Long userId, Long termId);

List<UserTerm> findByUserIdAndIsAgreed(Long userId, boolean isAgreed);

Optional<UserTerm> findByUserIdAndTermName(Long userId, TermName termName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ public enum UserErrorCode implements BaseErrorCode {
"매칭 상세 정보를 불러오는데 실패하였습니다."
),

USER_NOTIFICATION_SETTING_NOT_FOUND(
HttpStatus.NOT_FOUND,
"USER404_12",
"알림 설정이 존재하지 않습니다."
),


// 400 - 요청 오류
USER_UPDATE_BAD_REQUEST(
HttpStatus.BAD_REQUEST,
Expand Down Expand Up @@ -131,6 +138,12 @@ public enum UserErrorCode implements BaseErrorCode {
HttpStatus.BAD_REQUEST,
"USER400_9",
"페이지 크기는 1보다 커야 합니다."
),

INVALID_TERM(
HttpStatus.BAD_REQUEST,
"USER400_10",
"Term이 존재하지 않습니다."
);

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.example.RealMatch.user.presentation.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.RealMatch.global.config.jwt.CustomUserDetails;
import com.example.RealMatch.global.presentation.CustomResponse;
import com.example.RealMatch.user.application.service.NotificationSettingService;
import com.example.RealMatch.user.presentation.dto.request.NotificationSettingUpdateRequest;
import com.example.RealMatch.user.presentation.dto.response.NotificationSettingResponse;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/v1/users/me/notification-settings")
@RequiredArgsConstructor
public class NotificationSettingController {

private final NotificationSettingService notificationSettingService;

/**
* 내 알림 설정 조회
*/
@GetMapping
public CustomResponse<NotificationSettingResponse> getMyNotificationSetting(
@AuthenticationPrincipal CustomUserDetails userDetails
) {
return CustomResponse.ok(
notificationSettingService.getMySetting(userDetails.getUserId())
);
}

/**
* 내 알림 설정 전체 수정 (설정 완료 버튼)
*/
@PutMapping
public CustomResponse<String> updateMyNotificationSetting(
@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody NotificationSettingUpdateRequest request
) {
notificationSettingService.updateSetting(
userDetails.getUserId(),
request
);
return CustomResponse.ok("알림 설정이 수정되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.RealMatch.user.presentation.dto.request;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class NotificationSettingUpdateRequest {
private boolean marketingConsent;
private boolean appPushEnabled;
private boolean emailEnabled;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.RealMatch.user.presentation.dto.response;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class NotificationSettingResponse {
private boolean marketingConsent;
private boolean appPushEnabled;
private boolean emailEnabled;

}

Loading