Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -30,8 +30,14 @@ public void logout(
HttpServletResponse response
) {
logoutService.logout(refreshToken);

// 1. 현재 표준 쿠기 (Path=/) 삭제
response.addCookie(
CookieUtils.deleteRefreshTokenCookie(cookieSecure)
);

// 2. 레거시 쿠키(Path=/api/auth)도 삭제 (과거 잔재 청소)
response.addCookie(CookieUtils.deleteRefreshTokenCookie(cookieSecure, "/api/auth", "Lax"));
response.addCookie(CookieUtils.deleteRefreshTokenCookie(cookieSecure, "/api/auth", "Strict"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public OAuthLoginResponse login(
) {
OAuthLoginResult result = oAuthService.login(OAuthProvider.from(provider), code);

// 1. 레거시(/api/auth) 쿠키 제거 (예전 SameSite가 Strict였다면 Strict로 한 번 더)
response.addCookie(CookieUtils.deleteRefreshTokenCookie(cookieSecure, "/api/auth", "Strict"));
response.addCookie(CookieUtils.deleteRefreshTokenCookie(cookieSecure, "/api/auth", "Lax"));

// 2. 정상(/) 쿠키 설정
response.addCookie(
CookieUtils.createRefreshTokenCookie(
result.refreshToken(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,30 @@
import com.ureca.unity.domain.summary.dto.response.SummaryDetailResponse;
import com.ureca.unity.domain.summary.dto.response.SummaryListResponse;
import com.ureca.unity.domain.summary.service.SummaryService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(
name = "4. Summary",
description = "요약 관련 API"
)
@RestController
@RequestMapping("/api/summaries")
@RequiredArgsConstructor
public class SummaryController {

private final SummaryService summaryService;

// 전체 요약 리스트
// 전체 요약 목록
@GetMapping
public List<SummaryListResponse> getMySummaries(@RequestParam Long userId) {
return summaryService.getMySummaries(userId);
}

// 북마크 요약 리스트
// 북마크 요약 목록
@GetMapping("/bookmarks")
public List<SummaryListResponse> getBookmarkedSummaries(@RequestParam Long userId) {
return summaryService.getBookmarkedSummaries(userId);
Expand All @@ -32,4 +37,10 @@ public List<SummaryListResponse> getBookmarkedSummaries(@RequestParam Long userI
public SummaryDetailResponse getSummaryDetail(@PathVariable Long summaryId) {
return summaryService.getSummaryDetail(summaryId);
}

// 북마크 토글 (프론트가 PATCH로 호출 중)
@PatchMapping("/{summaryId}/bookmark")
public void toggleBookmark(@PathVariable Long summaryId) {
summaryService.toggleBookmark(summaryId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,9 @@ void updateBookmark(
@Param("isBookmarked") boolean isBookmarked
);

// 전체 목록
List<SummaryModel> findByUserId(@Param("userId") Long userId);

// 북마크 목록
List<SummaryModel> findBookmarkedByUserId(@Param("userId") Long userId);

// 상세
SummaryModel findById(@Param("summaryId") Long summaryId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ public class SummaryService {
private final ObjectMapper objectMapper;

@Transactional
public void createSummary(
Long counselingResultId,
Long userId,
String counselingText
) {
public void createSummary(Long counselingResultId, Long userId, String counselingText) {
if (userId == null) {
throw new CustomException(ErrorCode.INVALID_INPUT_VALUE);
}
Expand Down Expand Up @@ -61,13 +57,8 @@ public void createSummary(

summaryMapper.updateStatus(summaryId, "SUCCESS");

// 필요하면 반환형으로 바꾸거나, 로그용으로 사용
new SummaryResponse(
gemini.getTitle(),
gemini.getSubject(),
keywords,
points
);
// (현재는 반환값 사용 안 하니 생성만 유지)
new SummaryResponse(gemini.getTitle(), gemini.getSubject(), keywords, points);
Comment thread
joonhyong marked this conversation as resolved.

} catch (Exception e) {
summaryMapper.updateStatus(summaryId, "FAIL");
Expand All @@ -78,72 +69,31 @@ public void createSummary(
@Transactional(readOnly = true)
public List<SummaryListResponse> getMySummaries(Long userId) {
return summaryMapper.findByUserId(userId).stream()
.map(summary -> {
try {
List<String> keywords =
summary.getKeywords() != null
? objectMapper.readValue(summary.getKeywords(),
new TypeReference<List<String>>() {})
: List.of();

return new SummaryListResponse(
summary.getSummaryId(),
summary.getTitle(),
summary.getStatus(),
keywords,
summary.getCreatedAt()
);
} catch (Exception e) {
throw new IllegalStateException(e);
}
})
.map(this::toListResponse)
.toList();
}

@Transactional(readOnly = true)
public List<SummaryListResponse> getBookmarkedSummaries(Long userId) {
return summaryMapper.findBookmarkedByUserId(userId).stream()
.map(summary -> {
try {
List<String> keywords =
summary.getKeywords() != null
? objectMapper.readValue(summary.getKeywords(),
new TypeReference<List<String>>() {})
: List.of();

return new SummaryListResponse(
summary.getSummaryId(),
summary.getTitle(),
summary.getStatus(),
keywords,
summary.getCreatedAt()
);
} catch (Exception e) {
throw new IllegalStateException(e);
}
})
.map(this::toListResponse)
.toList();
}

@Transactional(readOnly = true)
public SummaryDetailResponse getSummaryDetail(Long summaryId) {
SummaryModel summary = summaryMapper.findById(summaryId);

if (summary == null) {
return null;
}
if (summary == null) return null;

try {
List<String> keywords =
summary.getKeywords() != null
? objectMapper.readValue(summary.getKeywords(),
new TypeReference<List<String>>() {})
? objectMapper.readValue(summary.getKeywords(), new TypeReference<List<String>>() {})
: List.of();

List<String> points =
summary.getPoints() != null
? objectMapper.readValue(summary.getPoints(),
new TypeReference<List<String>>() {})
? objectMapper.readValue(summary.getPoints(), new TypeReference<List<String>>() {})
: List.of();

return new SummaryDetailResponse(
Expand All @@ -156,7 +106,6 @@ public SummaryDetailResponse getSummaryDetail(Long summaryId) {
summary.getIsBookmarked(),
summary.getCreatedAt()
);

} catch (Exception e) {
throw new IllegalStateException(e);
}
Expand All @@ -165,11 +114,28 @@ public SummaryDetailResponse getSummaryDetail(Long summaryId) {
@Transactional
public void toggleBookmark(Long summaryId) {
Boolean isBookmarked = summaryMapper.findBookmarkStatus(summaryId);

if (isBookmarked == null) {
throw new IllegalArgumentException("summary not found");
}

summaryMapper.updateBookmark(summaryId, !isBookmarked);
}

private SummaryListResponse toListResponse(SummaryModel summary) {
try {
List<String> keywords =
summary.getKeywords() != null
? objectMapper.readValue(summary.getKeywords(), new TypeReference<List<String>>() {})
: List.of();

return new SummaryListResponse(
summary.getSummaryId(),
summary.getTitle(),
summary.getStatus(),
keywords,
summary.getCreatedAt()
);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 엔드포인트
.allowedOrigins(allowedOrigins.split("\\s*,\\s*"))
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowCredentials(true);
}
}
20 changes: 18 additions & 2 deletions src/main/java/com/ureca/unity/global/util/CookieUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static Cookie createRefreshTokenCookie(
cookie.setSecure(secure);
cookie.setPath(PATH);
cookie.setMaxAge((int) maxAgeSeconds);
cookie.setAttribute("SameSite", "Strict");
cookie.setAttribute("SameSite", "Lax");
return cookie;
}

Expand All @@ -31,7 +31,23 @@ public static Cookie deleteRefreshTokenCookie(boolean secure) {
cookie.setSecure(secure);
cookie.setPath(PATH);
cookie.setMaxAge(0);
cookie.setAttribute("SameSite", "Strict");
cookie.setAttribute("SameSite", "Lax");
return cookie;
}

public static Cookie deleteRefreshTokenCookie(boolean secure, String path, String sameSite) {
String safePath = (path == null || path.isBlank()) ? PATH : path;

String normalizedSameSite =
(sameSite != null && "Strict".equalsIgnoreCase(sameSite)) ? "Strict" : "Lax";

Cookie cookie = new Cookie(REFRESH_TOKEN, null);
cookie.setHttpOnly(true);
cookie.setSecure(secure);
cookie.setPath(safePath);
cookie.setMaxAge(0);
cookie.setAttribute("SameSite", normalizedSameSite);
return cookie;
Comment thread
joonhyong marked this conversation as resolved.
}

}
50 changes: 29 additions & 21 deletions src/main/resources/mapper/summary/SummaryMapper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<mapper namespace="com.ureca.unity.domain.summary.mapper.SummaryMapper">

<!-- 요약 생성 -->
<insert id="insertSummary" useGeneratedKeys="true" keyProperty="summaryId">
<insert id="insertSummary">
INSERT INTO summary (
counseling_result_id,
user_id,
Expand Down Expand Up @@ -37,8 +37,14 @@
SET
title = #{title},
subject = #{subject},
keywords = CAST(#{keywords} AS JSON),
points = CAST(#{points} AS JSON)
keywords = CASE
WHEN #{keywords} IS NULL THEN NULL
ELSE CAST(#{keywords} AS JSON)
END,
points = CASE
WHEN #{points} IS NULL THEN NULL
ELSE CAST(#{points} AS JSON)
END
WHERE summary_id = #{summaryId}
</update>

Expand All @@ -48,71 +54,73 @@
WHERE summary_id = #{summaryId}
</update>

<!-- 북마크 상태 -->
<select id="findBookmarkStatus" resultType="boolean">
SELECT is_bookmarked
FROM summary
WHERE summary_id = #{summaryId}
</select>

<!-- 북마크 토글 -->
<update id="updateBookmark">
UPDATE summary
SET is_bookmarked = #{isBookmarked}
WHERE summary_id = #{summaryId}
</update>

<!-- 전체 리스트 조회 -->
<!-- 전체 리스트 -->
<select id="findByUserId"
resultType="com.ureca.unity.domain.summary.model.SummaryModel">
SELECT
summary_id AS summaryId,
counseling_result_id AS counselingResultId,
user_id AS userId,
summary_id AS summaryId,
counseling_result_id AS counselingResultId,
user_id AS userId,
title,
subject,
keywords,
points,
is_bookmarked AS isBookmarked,
is_bookmarked AS isBookmarked,
status,
created_at AS createdAt
created_at AS createdAt
FROM summary
WHERE user_id = #{userId}
ORDER BY created_at DESC
</select>

<!-- 북마크 리스트 조회 -->
<!-- 북마크 리스트 -->
<select id="findBookmarkedByUserId"
resultType="com.ureca.unity.domain.summary.model.SummaryModel">
SELECT
summary_id AS summaryId,
counseling_result_id AS counselingResultId,
user_id AS userId,
summary_id AS summaryId,
counseling_result_id AS counselingResultId,
user_id AS userId,
title,
subject,
keywords,
points,
is_bookmarked AS isBookmarked,
is_bookmarked AS isBookmarked,
status,
created_at AS createdAt
created_at AS createdAt
FROM summary
WHERE user_id = #{userId}
AND is_bookmarked = true
ORDER BY created_at DESC
</select>

<!-- 요약 상세 조회 -->
<!-- 상세 -->
<select id="findById"
resultType="com.ureca.unity.domain.summary.model.SummaryModel">
SELECT
summary_id AS summaryId,
counseling_result_id AS counselingResultId,
user_id AS userId,
summary_id AS summaryId,
counseling_result_id AS counselingResultId,
user_id AS userId,
title,
subject,
keywords,
points,
is_bookmarked AS isBookmarked,
is_bookmarked AS isBookmarked,
status,
created_at AS createdAt
created_at AS createdAt
FROM summary
WHERE summary_id = #{summaryId}
</select>
Expand Down