Repository navigation
[feat] #30 - TODO 조회 기본/7일 #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
5bdc95e
feat: 사용자 언어 변경 기능 구현
aneykrap d7b5453
fix: 충돌 해결
aneykrap 1f69e38
refactor: 불필요한 공백 제거
aneykrap 387248d
fix: 충돌 해결
aneykrap 3cb4149
fix: Swagger 문서에 보안 요구 사항 추가 및 불필요한 코드 정리
aneykrap 06e58f7
feat: ReissueResult DTO 추가
Jy000n cddb4dd
feat: AuthService에 재발급 비즈니스 로직 추가
Jy000n 8bfe4a4
refactor: Auth 응답 코드 네이밍 수정
Jy000n fa3a27b
feat: 쿠키 생성/만료 공통 유틸 CookieUtil 추가
Jy000n 83db291
refactor: OAuthSuccessHandler 쿠키 생성 로직을 CookieUtil로 변경
Jy000n cc7a2f8
feat: AccessToken 재발급 API 구현 (refreshToken rotation 적용)
Jy000n b576c10
feat: 로그아웃 API 구현
Jy000n 81fce48
feat: 회원 탈퇴 API 구현
Jy000n b96407f
refactor: Auth API 경로 변경
Jy000n f34eeb4
feat: 로그아웃된 accessToken 블랙리스트 관리를 위한 BlacklistService 추가
Jy000n 989d4ab
feat: JwtTokenProvider에 getRemainingExpiry 메서드 추가
Jy000n d2f11a6
feat: JwtAuthenticationFilter에 블랙리스트 검증 추가
Jy000n 9424cbf
feat: 로그아웃 시 Access Token 블랙리스트 처리 추가
Jy000n 26ac5ed
feat: 회원 탈퇴 시 Access Token 블랙리스트 처리 추가
Jy000n 444f68f
fix: JwtAuthenticationFilter 블랙리스트 검증 로직 수정
Jy000n a24a68e
refactor: resolveToken을 TokenExtractor 유틸로 분리하여 중복 제거
Jy000n 23efe1c
chore: 불필요한 import 및 공백 제거
Jy000n c5d7457
fix: 쿠키 path를 /api/v1/auth로 수정
Jy000n b084b58
fix: 탈퇴 API 경로 변경
Jy000n d8d0791
refactor: 탈퇴 로직에서 사용자 존재 검증 순서 변경
Jy000n 1c8ea8b
refactor: 탈퇴 로직 트랜잭션 적용
Jy000n 81b2eae
fix: 만료된 Access Token은 블랙리스트에 저장하지 않도록 수정
Jy000n 493234d
refactor: Controller에서 Swagger docs 인터페이스 분리 적용
Jy000n d326e90
fix: createdAt JSON 직렬화 포맷 설정
Jy000n 29ebb29
fix: AuthControllerDocs token 메서드 파라미터 타입 불일치 수정
Jy000n 6bf7033
fix: ErrorDto timestamp Swagger 예시 포맷 수정
Jy000n 1ed8111
refactor: Swagger Auth API 문서 개선
Jy000n 90f5a27
refactor: AuthReissueResponse 생성
Jy000n 73db157
feat: code 필드에 입력 검증 어노테이션 추가
Jy000n c6c3022
refactor: 쿠키/응답 조립 로직을 AuthResponseFactory로 분리
Jy000n b6d314f
fix: token 요청에 @Valid 누락 수정
Jy000n 12be0e2
fix: 탈퇴 시 전체 세션 refresh token 삭제 및 재발급 시 유저 존재 검증 추가
Jy000n 6ebaea8
chore: AuthController의 미사용 cookieSecure 필드 제거
Jy000n a71066f
feat(todo): 투두 조회용 엔티티 보강 (타이머 상태·완료 여부 등)
laura-jung a430a6e
feat(user): 사용자별 시간대(zoneId) 설정 기능 추가
laura-jung 7c0589a
feat: UTC 시간대 기준 통일
laura-jung 85c2a7a
feat: 사용자별 시간대 설정 APT 문서/DTO 보강
laura-jung e0cff25
feat(home): 홈화면 조회 기능
laura-jung 3c6a918
Merge remote-tracking branch 'origin/develop' into feat/#30-getTodo
laura-jung e60b7ec
refactor(home): filter 빈값으로 들어올 시 defualt 반환으로 수정
laura-jung 9381897
refactor(tag): 태그 이름 필수 값 설정
laura-jung 03af13c
fix: UTC 시간대 설정을 main()으로 이동해 앱 시작 전 보장
laura-jung a0d10aa
refactor: 중복 어노테이션 @SecurityRequirement(name = "bearerAuth") 제거
laura-jung File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/main/java/com/Timo/Timo/domain/home/controller/HomeController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package com.Timo.Timo.domain.home.controller; | ||
|
|
||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| import com.Timo.Timo.domain.home.docs.HomeControllerDocs; | ||
| import com.Timo.Timo.domain.home.dto.response.HomeResponse; | ||
| import com.Timo.Timo.domain.home.exception.HomeSuccessCode; | ||
| import com.Timo.Timo.domain.home.service.HomeService; | ||
| import com.Timo.Timo.global.auth.principal.CustomUserDetails; | ||
| import com.Timo.Timo.global.response.BaseResponse; | ||
|
|
||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/v1/home") | ||
| @RequiredArgsConstructor | ||
| @Tag(name = "Home", description = "HOME API") | ||
| public class HomeController implements HomeControllerDocs { | ||
|
|
||
| private final HomeService homeService; | ||
|
|
||
| @Override | ||
| @GetMapping | ||
| public ResponseEntity<BaseResponse<HomeResponse>> getHome( | ||
| @AuthenticationPrincipal CustomUserDetails userDetails, | ||
| @RequestParam(required = false) String filter, | ||
| @RequestParam(required = false) String baseDate | ||
| ) { | ||
| HomeResponse response = homeService.getHome(userDetails.getUserId(), filter, baseDate); | ||
|
|
||
| return ResponseEntity | ||
| .status(HomeSuccessCode.GET_HOME.getHttpStatus()) | ||
| .body(BaseResponse.onSuccess(HomeSuccessCode.GET_HOME, response)); | ||
| } | ||
| } | ||
63 changes: 63 additions & 0 deletions
63
src/main/java/com/Timo/Timo/domain/home/docs/HomeControllerDocs.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.Timo.Timo.domain.home.docs; | ||
|
|
||
| import org.springframework.http.ResponseEntity; | ||
|
|
||
| import com.Timo.Timo.domain.home.dto.response.HomeResponse; | ||
| import com.Timo.Timo.global.auth.principal.CustomUserDetails; | ||
| import com.Timo.Timo.global.exception.dto.ErrorDto; | ||
| import com.Timo.Timo.global.response.BaseResponse; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.Parameter; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
|
|
||
| public interface HomeControllerDocs { | ||
|
|
||
| @Operation( | ||
| summary = "HOME 화면 TODO 목록 조회", | ||
| description = """ | ||
| HOME 화면의 필터 조건에 따라 일별 TODO 목록을 조회합니다. | ||
| DEFAULT는 기준 날짜 전후 7일, WEEK는 기준 날짜부터 7일을 반환합니다. | ||
| 각 날짜의 TODO는 미완료 먼저, 같은 완료 그룹 안에서는 sortOrder 오름차순으로 정렬됩니다. | ||
| """ | ||
| ) | ||
| @ApiResponses({ | ||
| @ApiResponse( | ||
| responseCode = "200", | ||
| description = "홈 뷰 조회 성공", | ||
| useReturnTypeSchema = true | ||
| ), | ||
| @ApiResponse( | ||
| responseCode = "400", | ||
| description = "유효하지 않은 필터 값이거나 날짜 형식이 올바르지 않은 경우", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorDto.class) | ||
| ) | ||
| ), | ||
| @ApiResponse( | ||
| responseCode = "401", | ||
| description = "Access Token이 없거나 만료되었거나 유효하지 않은 경우", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorDto.class) | ||
| ) | ||
| ), | ||
| @ApiResponse( | ||
| responseCode = "500", | ||
| description = "서버 내부 오류", | ||
| content = @Content( | ||
| mediaType = "application/json", | ||
| schema = @Schema(implementation = ErrorDto.class) | ||
| ) | ||
| ) | ||
| }) | ||
| ResponseEntity<BaseResponse<HomeResponse>> getHome( | ||
| @Parameter(hidden = true) CustomUserDetails userDetails, | ||
| @Parameter(description = "조회 필터(DEFAULT, WEEK)", example = "DEFAULT") String filter, | ||
| @Parameter(description = "기준 날짜(yyyy-MM-dd), 미입력 시 오늘", example = "2026-07-22") String baseDate | ||
| ); | ||
| } |
93 changes: 93 additions & 0 deletions
93
src/main/java/com/Timo/Timo/domain/home/dto/response/HomeResponse.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| package com.Timo.Timo.domain.home.dto.response; | ||
|
|
||
| import java.time.LocalDate; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| import com.Timo.Timo.domain.home.enums.HomeFilter; | ||
| import com.Timo.Timo.domain.tag.entity.Tag; | ||
| import com.Timo.Timo.domain.todo.entity.Subtask; | ||
| import com.Timo.Timo.domain.todo.enums.TodoTimerStatus; | ||
| import com.Timo.Timo.domain.todo.enums.Weekday; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
|
||
| public record HomeResponse( | ||
| HomeFilter filter, | ||
| LocalDate baseDate, | ||
| List<DayResponse> days | ||
| ) { | ||
|
|
||
| public record DayResponse( | ||
| LocalDate date, | ||
| Weekday dayOfWeek, | ||
| @JsonProperty("isHoliday") | ||
| boolean isHoliday, | ||
| @JsonProperty("isToday") | ||
| boolean isToday, | ||
| int totalCount, | ||
| int completedCount, | ||
| List<TodoResponse> todos | ||
| ) { | ||
| public static DayResponse of( | ||
| LocalDate date, | ||
| boolean isHoliday, | ||
| boolean isToday, | ||
| List<TodoResponse> todos | ||
| ) { | ||
| int completedCount = (int) todos.stream() | ||
| .filter(TodoResponse::completed) | ||
| .count(); | ||
|
|
||
| return new DayResponse( | ||
| date, | ||
| Weekday.from(date.getDayOfWeek()), | ||
| isHoliday, | ||
| isToday, | ||
| todos.size(), | ||
| completedCount, | ||
| todos | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| public record TodoResponse( | ||
| Long todoId, | ||
| String icon, | ||
| String title, | ||
| boolean completed, | ||
| Integer durationSeconds, | ||
| String priority, | ||
| TagResponse tag, | ||
| boolean hasMemo, | ||
| @JsonProperty("isRepeated") | ||
| boolean isRepeated, | ||
| TodoTimerStatus timerStatus, | ||
| Integer sortOrder, | ||
| List<SubtaskResponse> subtasks | ||
| ) { | ||
| } | ||
|
|
||
| public record TagResponse( | ||
| Long tagId, | ||
| String name | ||
| ) { | ||
| public static TagResponse from(Tag tag) { | ||
| Objects.requireNonNull(tag, "tag must not be null"); | ||
| return new TagResponse(tag.getId(), tag.getName()); | ||
| } | ||
| } | ||
|
|
||
| public record SubtaskResponse( | ||
| Long subtaskId, | ||
| String content, | ||
| boolean completed | ||
| ) { | ||
| public static SubtaskResponse from(Subtask subtask) { | ||
| return new SubtaskResponse( | ||
| subtask.getId(), | ||
| subtask.getContent(), | ||
| subtask.isCompleted() | ||
| ); | ||
| } | ||
| } | ||
| } |
6 changes: 6 additions & 0 deletions
6
src/main/java/com/Timo/Timo/domain/home/enums/HomeFilter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.Timo.Timo.domain.home.enums; | ||
|
|
||
| public enum HomeFilter { | ||
| DEFAULT, | ||
| WEEK | ||
| } |
20 changes: 20 additions & 0 deletions
20
src/main/java/com/Timo/Timo/domain/home/exception/HomeErrorCode.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.Timo.Timo.domain.home.exception; | ||
|
|
||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| import com.Timo.Timo.global.exception.code.BaseErrorCode; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum HomeErrorCode implements BaseErrorCode { | ||
|
|
||
| INVALID_FILTER_OR_DATE(HttpStatus.BAD_REQUEST, "HOME_400", "유효하지 않은 필터 값이거나 날짜 형식이 올바르지 않습니다."), | ||
| INVALID_DATE(HttpStatus.BAD_REQUEST, "DATE_400", "날짜 형식이 올바르지 않습니다."); | ||
|
|
||
| private final HttpStatus httpStatus; | ||
| private final String code; | ||
| private final String message; | ||
| } |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/Timo/Timo/domain/home/exception/HomeSuccessCode.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.Timo.Timo.domain.home.exception; | ||
|
|
||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| import com.Timo.Timo.global.exception.code.BaseSuccessCode; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum HomeSuccessCode implements BaseSuccessCode { | ||
|
|
||
| GET_HOME(HttpStatus.OK, "홈 뷰 조회 성공"); | ||
|
|
||
| private final HttpStatus httpStatus; | ||
| private final String message; | ||
| } |
61 changes: 61 additions & 0 deletions
61
src/main/java/com/Timo/Timo/domain/home/mapper/HomeTodoMapper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package com.Timo.Timo.domain.home.mapper; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import com.Timo.Timo.domain.home.dto.response.HomeResponse.SubtaskResponse; | ||
| import com.Timo.Timo.domain.home.dto.response.HomeResponse.TagResponse; | ||
| import com.Timo.Timo.domain.home.dto.response.HomeResponse.TodoResponse; | ||
| import com.Timo.Timo.domain.tag.entity.Tag; | ||
| import com.Timo.Timo.domain.todo.entity.Subtask; | ||
| import com.Timo.Timo.domain.todo.entity.Todo; | ||
| import com.Timo.Timo.domain.todo.entity.TodoInstance; | ||
| import com.Timo.Timo.domain.todo.enums.RepeatType; | ||
| import com.Timo.Timo.domain.todo.enums.TodoTimerStatus; | ||
|
|
||
| @Component | ||
| public class HomeTodoMapper { | ||
|
|
||
| public TodoResponse toResponse(TodoContext context) { | ||
| Todo todo = context.todo(); | ||
| TodoInstance instance = context.instance(); | ||
|
|
||
| return new TodoResponse( | ||
| todo.getId(), | ||
| todo.getIcon() != null ? todo.getIcon().name() : null, | ||
| todo.getTitle(), | ||
| instance != null && instance.isCompleted(), | ||
| todo.getDurationSeconds(), | ||
| todo.getPriority() != null ? todo.getPriority().name() : null, | ||
| mapTag(context.tag()), | ||
| todo.getMemo() != null && !todo.getMemo().isBlank(), | ||
| todo.getRepeatType() != RepeatType.NONE, | ||
| resolveTimerStatus(instance), | ||
| instance != null ? instance.getSortOrder() : context.defaultSortOrder(), | ||
| mapSubtasks(todo.getSubtasks()) | ||
| ); | ||
| } | ||
|
|
||
| private TagResponse mapTag(Tag tag) { | ||
| return tag != null ? TagResponse.from(tag) : null; | ||
| } | ||
|
|
||
| private TodoTimerStatus resolveTimerStatus(TodoInstance instance) { | ||
| return instance != null ? instance.getTimerStatus() : TodoTimerStatus.STOPPED; | ||
| } | ||
|
|
||
| private List<SubtaskResponse> mapSubtasks(List<Subtask> subtasks) { | ||
| return subtasks.stream() | ||
| .map(SubtaskResponse::from) | ||
| .toList(); | ||
| } | ||
|
|
||
| public record TodoContext( | ||
| Todo todo, | ||
| TodoInstance instance, | ||
| Tag tag, | ||
| int defaultSortOrder | ||
| ) { | ||
| } | ||
| } |
44 changes: 44 additions & 0 deletions
44
src/main/java/com/Timo/Timo/domain/home/service/HolidayChecker.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package com.Timo.Timo.domain.home.service; | ||
|
|
||
| import java.time.DayOfWeek; | ||
| import java.time.LocalDate; | ||
| import java.time.MonthDay; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
|
|
||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import com.Timo.Timo.domain.user.enums.Language; | ||
|
|
||
| @Component | ||
| public class HolidayChecker { | ||
|
|
||
| private static final Set<MonthDay> KOREAN_FIXED_HOLIDAYS = Set.of( | ||
| MonthDay.of(1, 1), | ||
| MonthDay.of(3, 1), | ||
| MonthDay.of(5, 5), | ||
| MonthDay.of(6, 6), | ||
| MonthDay.of(8, 15), | ||
| MonthDay.of(10, 3), | ||
| MonthDay.of(10, 9), | ||
| MonthDay.of(12, 25) | ||
| ); | ||
|
|
||
| /** | ||
| * 언어(지역)별 양력 고정 공휴일. 음력 공휴일(설날·추석 등)은 별도 계산이 필요해 아직 포함하지 않는다. | ||
| * 신뢰할 만한 데이터가 없는 언어는 빈 집합으로 두고 일요일만 공휴일로 처리한다. | ||
| */ | ||
| private static final Map<Language, Set<MonthDay>> FIXED_HOLIDAYS_BY_LANGUAGE = Map.of( | ||
| Language.KO, KOREAN_FIXED_HOLIDAYS, | ||
| Language.EN, Set.of() | ||
| ); | ||
|
|
||
| public boolean isHoliday(LocalDate date, Language language) { | ||
| if (date.getDayOfWeek() == DayOfWeek.SUNDAY) { | ||
| return true; | ||
| } | ||
|
|
||
| Set<MonthDay> fixedHolidays = FIXED_HOLIDAYS_BY_LANGUAGE.getOrDefault(language, Set.of()); | ||
| return fixedHolidays.contains(MonthDay.from(date)); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.