Skip to content

Commit 1a8ca8d

Browse files
authored
Merge pull request #245 from prgrms-web-devcourse-final-project/feature/badword-response-refactor(WR9-140)
Feature/badword response refactor(wr9 140)
2 parents c4f74c7 + d04d4d9 commit 1a8ca8d

File tree

6 files changed

+56
-31
lines changed

6 files changed

+56
-31
lines changed

src/main/java/io/crops/warmletter/domain/badword/controller/BadWordController.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.crops.warmletter.domain.badword.dto.request.CreateBadWordRequest;
55
import io.crops.warmletter.domain.badword.dto.request.UpdateBadWordRequest;
66
import io.crops.warmletter.domain.badword.dto.request.UpdateBadWordStatusRequest;
7+
import io.crops.warmletter.domain.badword.dto.response.BadWordResponse;
78
import io.crops.warmletter.domain.badword.dto.response.UpdateBadWordResponse;
89
import io.crops.warmletter.domain.badword.service.BadWordService;
910
import io.crops.warmletter.global.response.BaseResponse;
@@ -28,19 +29,18 @@ public class BadWordController {
2829

2930
@PostMapping
3031
@Operation(summary = "금칙어 등록", description = "금칙어 등록하는 API입니다.")
31-
public ResponseEntity<BaseResponse<Void>> createBadWord(@RequestBody @Valid CreateBadWordRequest request) {
32-
badWordService.createBadWord(request);
33-
return ResponseEntity.ok(BaseResponse.of(null, "금칙어 등록완료"));
32+
public ResponseEntity<BaseResponse<BadWordResponse>> createBadWord(@RequestBody @Valid CreateBadWordRequest request) {
33+
BadWordResponse response = badWordService.createBadWord(request);
34+
return ResponseEntity.ok(BaseResponse.of(response, "금칙어 등록완료"));
3435
}
3536

3637
@PatchMapping("/{badWordId}/status")
3738
@Operation(summary = "금칙어 상태변경", description = "금칙어 상태변경 활성여부 API입니다.")
38-
public ResponseEntity<BaseResponse<Void>> updateBadWordStatus(
39+
public ResponseEntity<BaseResponse<BadWordResponse>> updateBadWordStatus(
3940
@PathVariable Long badWordId,
4041
@RequestBody @Valid UpdateBadWordStatusRequest request) {
41-
42-
badWordService.updateBadWordStatus(badWordId, request);
43-
return ResponseEntity.ok(BaseResponse.of(null, "금칙어 상태 변경 완료"));
42+
BadWordResponse response = badWordService.updateBadWordStatus(badWordId, request);
43+
return ResponseEntity.ok(BaseResponse.of(response, "금칙어 상태 변경 완료"));
4444
}
4545

4646
@GetMapping

src/main/java/io/crops/warmletter/domain/badword/dto/response/BadWordResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.crops.warmletter.domain.badword.dto.response;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
34
import io.swagger.v3.oas.annotations.media.Schema;
45
import lombok.AllArgsConstructor;
56
import lombok.Getter;
@@ -11,4 +12,11 @@ public class BadWordResponse {
1112
private Long id;
1213
@Schema(description = "금지어 단어", example = "비속어")
1314
private String word;
15+
16+
private boolean isUsed;
17+
18+
@JsonProperty("isUsed")
19+
public boolean isUsed() {
20+
return isUsed;
21+
}
1422
}

src/main/java/io/crops/warmletter/domain/badword/repository/BadWordRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
public interface BadWordRepository extends JpaRepository<BadWord, Long> {
1313
boolean existsByWord(String word);
1414

15-
@Query("SELECT new io.crops.warmletter.domain.badword.dto.response.BadWordResponse(b.id, b.word) FROM BadWord b WHERE b.isUsed = true")
15+
@Query("SELECT new io.crops.warmletter.domain.badword.dto.response.BadWordResponse(b.id, b.word, b.isUsed) FROM BadWord b ")
1616
List<BadWordResponse> findAllBadWords();
1717

1818
}

src/main/java/io/crops/warmletter/domain/badword/service/BadWordService.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.crops.warmletter.domain.badword.dto.request.CreateBadWordRequest;
55
import io.crops.warmletter.domain.badword.dto.request.UpdateBadWordRequest;
66
import io.crops.warmletter.domain.badword.dto.request.UpdateBadWordStatusRequest;
7+
import io.crops.warmletter.domain.badword.dto.response.BadWordResponse;
78
import io.crops.warmletter.domain.badword.dto.response.UpdateBadWordResponse;
89
import io.crops.warmletter.domain.badword.entity.BadWord;
910
import io.crops.warmletter.domain.badword.exception.BadWordContainsException;
@@ -33,7 +34,8 @@ public class BadWordService {
3334
private static final String BAD_WORD_PATTERN = "[^가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9\\s]";
3435

3536

36-
public void createBadWord(CreateBadWordRequest request) {
37+
@Transactional
38+
public BadWordResponse createBadWord(CreateBadWordRequest request) {
3739
String word = request.getWord();
3840

3941
boolean exists = badWordRepository.existsByWord(word);
@@ -46,38 +48,43 @@ public void createBadWord(CreateBadWordRequest request) {
4648
.isUsed(true)
4749
.build();
4850

49-
50-
badWordRepository.save(badWord);
51+
BadWord savedBadWord = badWordRepository.save(badWord);
5152

5253
redisTemplate.opsForHash().put(BAD_WORD_KEY, badWord.getId().toString(), word);
54+
return new BadWordResponse(savedBadWord.getId(), savedBadWord.getWord(), savedBadWord.isUsed());
5355
}
5456

5557

5658
@Transactional
57-
public void updateBadWordStatus(Long badWordId, UpdateBadWordStatusRequest request) {
59+
public BadWordResponse updateBadWordStatus(Long badWordId, UpdateBadWordStatusRequest request) {
5860
BadWord badWord = badWordRepository.findById(badWordId)
5961
.orElseThrow(BadWordNotFoundException::new);
6062
badWord.updateStatus(request.isUsed());
63+
BadWord savedBadWord = badWordRepository.save(badWord);
64+
redisTemplate.opsForHash().delete(BAD_WORD_KEY,badWordId.toString(), badWord.getWord());
65+
redisTemplate.opsForHash().put(BAD_WORD_KEY,badWordId.toString(), badWord.getWord());
66+
return new BadWordResponse(savedBadWord.getId(), savedBadWord.getWord(), savedBadWord.isUsed());
6167

62-
if (request.isUsed()) {
63-
redisTemplate.opsForHash().put(BAD_WORD_KEY,badWordId.toString(), badWord.getWord());
64-
} else {
65-
redisTemplate.opsForHash().delete(BAD_WORD_KEY,badWordId.toString(), badWord.getWord());
66-
}
6768
}
6869

6970
public List<Map<String, String>> getBadWords() {
7071
Map<Object, Object> entries = redisTemplate.opsForHash().entries(BAD_WORD_KEY);
7172
return entries.entrySet().stream()
7273
.map(e -> {
7374
Map<String, String> map = new HashMap<>();
74-
map.put("id", e.getKey().toString());
75+
// 여기서 id 변수를 선언합니다.
76+
String id = e.getKey().toString();
77+
map.put("id", id);
7578
map.put("word", e.getValue().toString());
79+
// DB에서 조회한 isUsed 값을 포함 (없으면 기본값 false)
80+
Optional<BadWord> optional = badWordRepository.findById(Long.valueOf(id));
81+
map.put("isUsed", optional.map(bw -> Boolean.toString(bw.isUsed())).orElse("false"));
7682
return map;
7783
})
78-
.toList();
84+
.collect(Collectors.toList());
7985
}
8086

87+
8188
@Transactional
8289
public UpdateBadWordResponse updateBadWord(Long id, UpdateBadWordRequest request) {
8390
BadWord badWord = badWordRepository.findById(id)

src/test/java/io/crops/warmletter/domain/badword/cache/BadWordCacheInitializerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ void loadBadWordsRedis_WithWords() {
4646
Long id2 = 2L;
4747
// given
4848
List<BadWordResponse> badWords = List.of(
49-
new BadWordResponse(id1, "시발"),
50-
new BadWordResponse(id2, "병신")
49+
new BadWordResponse(id1, "시발", true),
50+
new BadWordResponse(id2, "병신", true)
5151
);
5252
when(badWordRepository.findAllBadWords()).thenReturn(badWords);
5353

src/test/java/io/crops/warmletter/domain/badword/service/BadWordServiceTest.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,39 +131,52 @@ void updateBadWordStatus_activate_success() {
131131
.build();
132132
UpdateBadWordStatusRequest request = new UpdateBadWordStatusRequest(true);
133133

134+
// badWordRepository.findById() 호출에 대해 badWord 반환
134135
when(badWordRepository.findById(1L)).thenReturn(Optional.of(badWord));
136+
// badWordRepository.save() 호출 시 입력된 객체에 ID를 설정하여 반환하도록 스텁 설정
137+
when(badWordRepository.save(any(BadWord.class))).thenAnswer(invocation -> {
138+
BadWord updated = invocation.getArgument(0);
139+
// 예시로 ID를 1L로 설정
140+
ReflectionTestUtils.setField(updated, "id", 1L);
141+
return updated;
142+
});
143+
135144
// when
136145
badWordService.updateBadWordStatus(1L, request);
137146

138147
// then
139148
assertTrue(badWord.isUsed());
140-
// 서비스 코드에서 hashOperations.put 호출하므로 해당 호출 검증
149+
// hashOperations.put 호출 검증 (활성화 상태이므로 Redis에 업데이트)
141150
verify(hashOperations).put("bad_word", "1", "비속어");
142151
}
143152

144-
145153
@Test
146154
@DisplayName("금칙어 상태 업데이트 - 비활성화 성공")
147155
void updateBadWordStatus_deactivate_success() {
148156
// given
149157
BadWord badWord = BadWord.builder()
150-
.id(1L) // ID를 직접 할당
158+
.id(1L) // ID 직접 할당
151159
.word("비속어")
152160
.isUsed(true)
153161
.build();
154162

155163
UpdateBadWordStatusRequest request = new UpdateBadWordStatusRequest(false);
156164

157-
// Mock 동작 설정
165+
// findById는 기존 badWord를 반환하도록 설정
158166
when(badWordRepository.findById(1L)).thenReturn(Optional.of(badWord));
159-
// when
167+
// save() 호출 시 업데이트된 객체를 반환하도록 스텁 설정 (이 경우 ID는 그대로 1L)
168+
when(badWordRepository.save(any(BadWord.class))).thenAnswer(invocation -> invocation.getArgument(0));
169+
170+
// when: 상태 업데이트 메서드 호출 (비활성화)
160171
badWordService.updateBadWordStatus(1L, request);
161172

162173
// then
163-
assertFalse(badWord.isUsed()); // 상태 업데이트 확인
174+
// 상태가 false로 변경되었는지 검증
175+
assertFalse(badWord.isUsed());
176+
// Redis 캐시에서 해당 금칙어 삭제가 호출되었는지 검증
164177
verify(hashOperations).delete("bad_word", "1", "비속어");
165178
}
166-
//
179+
167180
@Test
168181
@DisplayName("금칙어가 포함되어 있지 않을 때 정상 통과")
169182
void validateText_noBadWord_success() {
@@ -286,7 +299,6 @@ void deleteBadWord_success() {
286299
// given
287300
Long badWordId = 1L;
288301
BadWord badWord = new BadWord();
289-
// 필요한 경우 badWord에 속성을 추가할 수 있습니다.
290302
when(badWordRepository.findById(badWordId)).thenReturn(Optional.of(badWord));
291303

292304
// when
@@ -311,15 +323,13 @@ void deleteBadWord_notFound() {
311323
@Test
312324
@DisplayName("금칙어가 포함된 경우 - 예외 발생")
313325
void testValidateTextWithBadWord() {
314-
// 테스트용 텍스트: 금칙어가 독립된 단어로 존재하도록 공백을 둡니다.
315326
String targetStr = "이 문장에는 금칙어0 가 포함되어 있습니다.";
316327
System.out.println("targetStr: " + targetStr);
317328

318329
// 해당 테스트에서 사용할 모의 금칙어 데이터: 오직 "금칙어0"만 포함
319330
Map<Object, Object> testBadWords = new HashMap<>();
320331
testBadWords.put("1", "금칙어0");
321332

322-
// 테스트 케이스 내에서 모의 데이터를 재설정(오버라이드)합니다.
323333
when(hashOperations.entries(BAD_WORD_KEY)).thenReturn(testBadWords);
324334
assertThrows(BadWordContainsException.class, () -> {
325335
badWordService.validateText(targetStr);

0 commit comments

Comments
 (0)