From 6757337b309c4d68f84d061afc6684a32a9dfc3b Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 03:43:33 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20CalendarConnection=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/entity/CalendarConnection.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java new file mode 100644 index 00000000..d638a5c2 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java @@ -0,0 +1,65 @@ +package com.Timo.Timo.domain.calendar.entity; + +import com.Timo.Timo.domain.user.entity.User; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "calendar_connections") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CalendarConnection { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @OneToOne + @Column(name = "user_id", nullable = false, unique = true) + private User user; + + @Column(name = "calendar_email", nullable = false) + private String calendarEmail; + + @Convert(converter = EncryptedStringConverter.class) + @Column(name = "access_token", nullable = false, length = 2048) + private String accessToken; + + @Convert(converter = EncryptedStringConverter.class) + @Column(name = "refresh_token", length = 2048) + private String refreshToken; + + @Column(name = "token_expires_at") + private LocalDateTime tokenExpiresAt; + + @Column(name = "connected_at", nullable = false) + private LocalDateTime connectedAt; + + @Builder + private CalendarConnection( + User user, + String calendarEmail, + String accessToken, + String refreshToken, + LocalDateTime tokenExpiresAt + ) { + this.user = user; + this.calendarEmail = calendarEmail; + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.tokenExpiresAt = tokenExpiresAt; + this.connectedAt = LocalDateTime.now(); + } +} From 124150774b266a3e83aee596ab4665833a531b93 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 03:47:17 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20Err?= =?UTF-8?q?orCode,=20SuccessCode=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/entity/CalendarConnection.java | 2 -- .../calendar/exception/CalendarErrorCode.java | 19 +++++++++++++++++++ .../exception/CalendarSuccessCode.java | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarSuccessCode.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java index d638a5c2..e9c468e3 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java @@ -33,11 +33,9 @@ public class CalendarConnection { @Column(name = "calendar_email", nullable = false) private String calendarEmail; - @Convert(converter = EncryptedStringConverter.class) @Column(name = "access_token", nullable = false, length = 2048) private String accessToken; - @Convert(converter = EncryptedStringConverter.class) @Column(name = "refresh_token", length = 2048) private String refreshToken; diff --git a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java new file mode 100644 index 00000000..e2cd2b98 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java @@ -0,0 +1,19 @@ +package com.Timo.Timo.domain.calendar.exception; + +import com.Timo.Timo.global.exception.code.BaseErrorCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum CalendarErrorCode implements BaseErrorCode { + + CALENDAR_401_AUTH_FAILED(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "구글 캘린더 인증에 실패했습니다."), + CALENDAR_404_NOT_CONNECTED(HttpStatus.NOT_FOUND, "CALENDAR_404", "연동된 캘린더가 없습니다."), + CALENDAR_409_ALREADY_CONNECTED(HttpStatus.CONFLICT, "CALENDAR_409", "이미 캘린더가 연동되어 있습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; +} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarSuccessCode.java b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarSuccessCode.java new file mode 100644 index 00000000..ca23f0dc --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarSuccessCode.java @@ -0,0 +1,17 @@ +package com.Timo.Timo.domain.calendar.exception; + +import com.Timo.Timo.global.exception.code.BaseSuccessCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum CalendarSuccessCode implements BaseSuccessCode { + + CALENDAR_CONNECTED(HttpStatus.CREATED, "구글 캘린더가 연동되었습니다."), + CALENDAR_DISCONNECTED(HttpStatus.OK, "구글 캘린더 연동이 해제되었습니다."); + + private final HttpStatus httpStatus; + private final String message; +} From ffc1855d541aa7fecc56be30e3da677be25af2bf Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 03:48:39 +0900 Subject: [PATCH 03/19] =?UTF-8?q?feat:=20CalendarConnectionRepository=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CalendarConnectionRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java b/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java new file mode 100644 index 00000000..26014187 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java @@ -0,0 +1,12 @@ +package com.Timo.Timo.domain.calendar.repository; + +import com.Timo.Timo.domain.calendar.entity.CalendarConnection; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CalendarConnectionRepository extends JpaRepository { + + Optional findByUserId(Long userId); + + boolean existsByUserId(Long userId); +} From a9a8aff68080ce8c7b4d28265e5b08bc4a543c64 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 03:50:52 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CalendarConnectRequest.java | 10 ++++++++++ .../dto/response/CalendarConnectResponse.java | 13 +++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java new file mode 100644 index 00000000..e8093fa8 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java @@ -0,0 +1,10 @@ +package com.Timo.Timo.domain.calendar.dto.request; + +import jakarta.validation.constraints.NotBlank; + +public record CalendarConnectRequest( + @NotBlank(message = "authorizationCode는 필수입니다.") + String authorizationCode +) { + +} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java new file mode 100644 index 00000000..2c9e9c97 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java @@ -0,0 +1,13 @@ +package com.Timo.Timo.domain.calendar.dto.response; + +import java.time.LocalDateTime; +import lombok.Builder; + +@Builder +public record CalendarConnectResponse( + boolean calendarConnected, + String connectedEmail, + LocalDateTime connectedAt +) { + +} From 138f43dacfc64cc3dcd8a7d9a518187abac3ee9d Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 03:51:43 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=ED=95=B4=EC=A0=9C=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/CalendarDisconnectResponse.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java new file mode 100644 index 00000000..e698a6a2 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java @@ -0,0 +1,10 @@ +package com.Timo.Timo.domain.calendar.dto.response; + +import lombok.Builder; + +@Builder +public record CalendarDisconnectResponse( + boolean calendarConnected +) { + +} From 060228356b588833f6d79f304be99198fb8d8531 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 03:55:31 +0900 Subject: [PATCH 06/19] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B8=80=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EA=B5=90=ED=99=98=20=EC=9D=91=EB=8B=B5=20DTO=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/dto/response/GoogleTokenResponse.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java new file mode 100644 index 00000000..2aaf5ab3 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java @@ -0,0 +1,9 @@ +package com.Timo.Timo.domain.calendar.dto.response; + +public record GoogleTokenResponse ( + String accessToken, + String refreshToken, + Integer expiresIn +){ + +} From 9dba14ae3e286062d2994d91ed0847492261bd60 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 04:05:19 +0900 Subject: [PATCH 07/19] =?UTF-8?q?feat:=20CalendarErrorCode=EC=97=90=20?= =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B6=88=EC=9D=BC=EC=B9=98=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Timo/Timo/domain/calendar/exception/CalendarErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java index e2cd2b98..516a28c2 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java @@ -10,6 +10,7 @@ public enum CalendarErrorCode implements BaseErrorCode { CALENDAR_401_AUTH_FAILED(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "구글 캘린더 인증에 실패했습니다."), + CALENDAR_401_EMAIL_MISMATCH(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "가입 시 사용한 구글 계정으로만 연동할 수 있습니다."), CALENDAR_404_NOT_CONNECTED(HttpStatus.NOT_FOUND, "CALENDAR_404", "연동된 캘린더가 없습니다."), CALENDAR_409_ALREADY_CONNECTED(HttpStatus.CONFLICT, "CALENDAR_409", "이미 캘린더가 연동되어 있습니다."); From dca234c0c4c51f6f98ad55fa9b63c9b30cf5b4f2 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 04:09:21 +0900 Subject: [PATCH 08/19] =?UTF-8?q?fix:=20CalendarConnectResponse=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/calendar/dto/response/CalendarConnectResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java index 2c9e9c97..25fd34f1 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java @@ -6,7 +6,7 @@ @Builder public record CalendarConnectResponse( boolean calendarConnected, - String connectedEmail, + String calendarEmail, LocalDateTime connectedAt ) { From 8d1696d029cf9440169ef7ee6973b96d3fd6aef9 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 04:25:51 +0900 Subject: [PATCH 09/19] =?UTF-8?q?feat:=20GoogleUserInfoResponse=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/dto/response/GoogleUserInfoResponse.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java new file mode 100644 index 00000000..badfdbb6 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java @@ -0,0 +1,7 @@ +package com.Timo.Timo.domain.calendar.dto.response; + +public record GoogleUserInfoResponse( + String email +) { + +} From 85f793bfd757f846b77c6e9ec8279d8034cc2395 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 04:28:37 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20GoogleOAuthClient=EB=A1=9C=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B5=90=ED=99=98/=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C/revoke=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/client/GoogleOAuthClient.java | 68 +++++++++++++++++++ .../calendar/exception/CalendarErrorCode.java | 6 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/client/GoogleOAuthClient.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/client/GoogleOAuthClient.java b/src/main/java/com/Timo/Timo/domain/calendar/client/GoogleOAuthClient.java new file mode 100644 index 00000000..08402061 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/client/GoogleOAuthClient.java @@ -0,0 +1,68 @@ +package com.Timo.Timo.domain.calendar.client; + +import com.Timo.Timo.domain.calendar.dto.response.GoogleTokenResponse; +import com.Timo.Timo.domain.calendar.dto.response.GoogleUserInfoResponse; +import com.Timo.Timo.domain.calendar.exception.CalendarErrorCode; +import com.Timo.Timo.global.exception.CustomException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; + +@Component +public class GoogleOAuthClient { + + private final RestClient restClient = RestClient.create(); + + @Value("${spring.security.oauth2.client.registration.google.client-id}") + private String clientId; + + @Value("${spring.security.oauth2.client.registration.google.client-secret}") + private String clientSecret; + + @Value("${app.oauth2.redirect-uri}") + private String redirectUri; + + public GoogleTokenResponse exchangeToken(String authorizationCode){ + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("code", authorizationCode); + body.add("client_id", clientId); + body.add("client_secret", clientSecret); + body.add("redirect_uri", redirectUri); + body.add("grant_type", "authorization_code"); + + try { + return restClient.post() + .uri("https://oauth2.googleapis.com/token") + .body(body) + .retrieve() + .body(GoogleTokenResponse.class); + } catch (Exception e) { + throw new CustomException(CalendarErrorCode.CALENDAR_401_AUTH_FAILED); + } + } + + public GoogleUserInfoResponse fetchUserInfo(String accessToken){ + try { + return restClient.get() + .uri("https://www.googleapis.com/oauth2/v2/userinfo") + .header("Authorization", "Bearer " + accessToken) + .retrieve() + .body(GoogleUserInfoResponse.class); + } catch (Exception e) { + throw new CustomException(CalendarErrorCode.CALENDAR_401_AUTH_FAILED); + } + } + + public void revokeToken(String token) { + try { + restClient.post() + .uri("https://oauth2.googleapis.com/revoke?token=" + token) + .retrieve() + .toBodilessEntity(); + } catch (Exception e) { + throw new CustomException(CalendarErrorCode.CALENDAR_500_REVOKE_FAILED); + } + } +} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java index 516a28c2..e9f149b7 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java @@ -11,8 +11,12 @@ public enum CalendarErrorCode implements BaseErrorCode { CALENDAR_401_AUTH_FAILED(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "구글 캘린더 인증에 실패했습니다."), CALENDAR_401_EMAIL_MISMATCH(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "가입 시 사용한 구글 계정으로만 연동할 수 있습니다."), + CALENDAR_404_NOT_CONNECTED(HttpStatus.NOT_FOUND, "CALENDAR_404", "연동된 캘린더가 없습니다."), - CALENDAR_409_ALREADY_CONNECTED(HttpStatus.CONFLICT, "CALENDAR_409", "이미 캘린더가 연동되어 있습니다."); + + CALENDAR_409_ALREADY_CONNECTED(HttpStatus.CONFLICT, "CALENDAR_409", "이미 캘린더가 연동되어 있습니다."), + + CALENDAR_500_REVOKE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "CALENDAR_500", "구글 토큰 해제에 실패했습니다."); private final HttpStatus httpStatus; private final String code; From b26dad257c1190f3c4a5d599ae7225e1f94ad384 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 04:43:42 +0900 Subject: [PATCH 11/19] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import,=20=EB=B9=88=EC=B9=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/calendar/dto/request/CalendarConnectRequest.java | 4 +--- .../domain/calendar/dto/response/CalendarConnectResponse.java | 4 +--- .../calendar/dto/response/CalendarDisconnectResponse.java | 4 +--- .../domain/calendar/dto/response/GoogleTokenResponse.java | 4 +--- .../domain/calendar/dto/response/GoogleUserInfoResponse.java | 4 +--- .../Timo/Timo/domain/calendar/entity/CalendarConnection.java | 1 - .../Timo/domain/calendar/exception/CalendarErrorCode.java | 3 --- .../calendar/repository/CalendarConnectionRepository.java | 1 - 8 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java index e8093fa8..4f2eea05 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/request/CalendarConnectRequest.java @@ -5,6 +5,4 @@ public record CalendarConnectRequest( @NotBlank(message = "authorizationCode는 필수입니다.") String authorizationCode -) { - -} +) {} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java index 25fd34f1..ab847ae9 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java @@ -8,6 +8,4 @@ public record CalendarConnectResponse( boolean calendarConnected, String calendarEmail, LocalDateTime connectedAt -) { - -} +) {} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java index e698a6a2..f35a53ca 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarDisconnectResponse.java @@ -5,6 +5,4 @@ @Builder public record CalendarDisconnectResponse( boolean calendarConnected -) { - -} +) {} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java index 2aaf5ab3..986fe260 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleTokenResponse.java @@ -4,6 +4,4 @@ public record GoogleTokenResponse ( String accessToken, String refreshToken, Integer expiresIn -){ - -} +){} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java index badfdbb6..00b684dc 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/GoogleUserInfoResponse.java @@ -2,6 +2,4 @@ public record GoogleUserInfoResponse( String email -) { - -} +) {} diff --git a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java index e9c468e3..e6afc6ca 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java @@ -2,7 +2,6 @@ import com.Timo.Timo.domain.user.entity.User; import jakarta.persistence.Column; -import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; diff --git a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java index e9f149b7..d837b1be 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/exception/CalendarErrorCode.java @@ -11,11 +11,8 @@ public enum CalendarErrorCode implements BaseErrorCode { CALENDAR_401_AUTH_FAILED(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "구글 캘린더 인증에 실패했습니다."), CALENDAR_401_EMAIL_MISMATCH(HttpStatus.UNAUTHORIZED, "CALENDAR_401", "가입 시 사용한 구글 계정으로만 연동할 수 있습니다."), - CALENDAR_404_NOT_CONNECTED(HttpStatus.NOT_FOUND, "CALENDAR_404", "연동된 캘린더가 없습니다."), - CALENDAR_409_ALREADY_CONNECTED(HttpStatus.CONFLICT, "CALENDAR_409", "이미 캘린더가 연동되어 있습니다."), - CALENDAR_500_REVOKE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "CALENDAR_500", "구글 토큰 해제에 실패했습니다."); private final HttpStatus httpStatus; diff --git a/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java b/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java index 26014187..5ef2ec9f 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/repository/CalendarConnectionRepository.java @@ -7,6 +7,5 @@ public interface CalendarConnectionRepository extends JpaRepository { Optional findByUserId(Long userId); - boolean existsByUserId(Long userId); } From c5a262a96abf971bd81dc0bd9bf6d004467f97bc Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 05:42:09 +0900 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B8=80=20=EC=BA=98?= =?UTF-8?q?=EB=A6=B0=EB=8D=94=20=EC=97=B0=EB=8F=99/=ED=95=B4=EC=A0=9C=20Se?= =?UTF-8?q?rvice=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20(=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9D=BC=EC=B9=98=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=ED=8F=AC=ED=95=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/service/CalendarService.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/service/CalendarService.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/service/CalendarService.java b/src/main/java/com/Timo/Timo/domain/calendar/service/CalendarService.java new file mode 100644 index 00000000..31337769 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/service/CalendarService.java @@ -0,0 +1,78 @@ +package com.Timo.Timo.domain.calendar.service; + +import com.Timo.Timo.domain.calendar.client.GoogleOAuthClient; +import com.Timo.Timo.domain.calendar.dto.request.CalendarConnectRequest; +import com.Timo.Timo.domain.calendar.dto.response.CalendarConnectResponse; +import com.Timo.Timo.domain.calendar.dto.response.CalendarDisconnectResponse; +import com.Timo.Timo.domain.calendar.dto.response.GoogleTokenResponse; +import com.Timo.Timo.domain.calendar.dto.response.GoogleUserInfoResponse; +import com.Timo.Timo.domain.calendar.entity.CalendarConnection; +import com.Timo.Timo.domain.calendar.exception.CalendarErrorCode; +import com.Timo.Timo.domain.calendar.repository.CalendarConnectionRepository; +import com.Timo.Timo.domain.user.entity.User; +import com.Timo.Timo.domain.user.exception.UserErrorCode; +import com.Timo.Timo.domain.user.repository.UserRepository; +import com.Timo.Timo.global.exception.CustomException; +import java.time.LocalDateTime; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CalendarService { + + private final CalendarConnectionRepository calendarConnectionRepository; + private final UserRepository userRepository; + private final GoogleOAuthClient googleOAuthClient; + + @Transactional + public CalendarConnectResponse connect(Long userId, CalendarConnectRequest request) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new CustomException(UserErrorCode.USER_NOT_FOUND)); + + if (calendarConnectionRepository.existsByUserId(userId)) { + throw new CustomException(CalendarErrorCode.CALENDAR_409_ALREADY_CONNECTED); + } + + GoogleTokenResponse tokenResponse = googleOAuthClient.exchangeToken(request.authorizationCode()); + GoogleUserInfoResponse userInfo = googleOAuthClient.fetchUserInfo(tokenResponse.accessToken()); + + validateSameAccount(user, userInfo); + + CalendarConnection calendarConnection = CalendarConnection.builder() + .user(user) + .calendarEmail(userInfo.email()) + .accessToken(tokenResponse.accessToken()) + .refreshToken(tokenResponse.refreshToken()) + .tokenExpiresAt(LocalDateTime.now().plusSeconds(tokenResponse.expiresIn())) + .build(); + + calendarConnectionRepository.save(calendarConnection); + + return CalendarConnectResponse.builder() + .calendarConnected(true) + .calendarEmail(calendarConnection.getCalendarEmail()) + .connectedAt(calendarConnection.getConnectedAt()) + .build(); + } + + private void validateSameAccount(User user, GoogleUserInfoResponse userInfo) { + if (!user.getEmail().equalsIgnoreCase(userInfo.email())) { + throw new CustomException(CalendarErrorCode.CALENDAR_401_EMAIL_MISMATCH); + } + } + + @Transactional + public CalendarDisconnectResponse disconnect(Long userId) { + CalendarConnection calendarConnection = calendarConnectionRepository.findByUserId(userId) + .orElseThrow(() -> new CustomException(CalendarErrorCode.CALENDAR_404_NOT_CONNECTED)); + + googleOAuthClient.revokeToken(calendarConnection.getAccessToken()); + calendarConnectionRepository.delete(calendarConnection); + + return CalendarDisconnectResponse.builder() + .calendarConnected(false) + .build(); + } +} From d0ad363e21b7aa481f0c71dc7e577abe8da1ac16 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 05:43:06 +0900 Subject: [PATCH 13/19] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99/=ED=95=B4=EC=A0=9C=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=A1=B0=EB=A6=BD=EC=9D=84=20=EC=9C=84=ED=95=9C=20CalendarResp?= =?UTF-8?q?onseFactory=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../factory/CalendarResponseFactory.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/factory/CalendarResponseFactory.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/factory/CalendarResponseFactory.java b/src/main/java/com/Timo/Timo/domain/calendar/factory/CalendarResponseFactory.java new file mode 100644 index 00000000..a0343b99 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/factory/CalendarResponseFactory.java @@ -0,0 +1,27 @@ +package com.Timo.Timo.domain.calendar.factory; + +import com.Timo.Timo.domain.calendar.dto.response.CalendarConnectResponse; +import com.Timo.Timo.domain.calendar.dto.response.CalendarDisconnectResponse; +import com.Timo.Timo.domain.calendar.exception.CalendarSuccessCode; +import com.Timo.Timo.global.response.BaseResponse; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +@Component +public class CalendarResponseFactory { + + public ResponseEntity> connectResponse( + CalendarConnectResponse response + ) { + return ResponseEntity.status(CalendarSuccessCode.CALENDAR_CONNECTED.getHttpStatus()) + .body(BaseResponse.onSuccess(CalendarSuccessCode.CALENDAR_CONNECTED, response)); + } + + public ResponseEntity> disconnectResponse( + CalendarDisconnectResponse response + ) { + return ResponseEntity.ok( + BaseResponse.onSuccess(CalendarSuccessCode.CALENDAR_DISCONNECTED, response) + ); + } +} From 84a388a7181462106a8b6f66e97a7ec60905cbe0 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 05:44:57 +0900 Subject: [PATCH 14/19] =?UTF-8?q?docs:=20CalendarControllerDocs=20Swagger?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/docs/CalendarControllerDocs.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java b/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java new file mode 100644 index 00000000..a44037c8 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java @@ -0,0 +1,65 @@ +package com.Timo.Timo.domain.calendar.docs; + +import com.Timo.Timo.domain.calendar.dto.request.CalendarConnectRequest; +import com.Timo.Timo.domain.calendar.dto.response.CalendarConnectResponse; +import com.Timo.Timo.domain.calendar.dto.response.CalendarDisconnectResponse; +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; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; + +public interface CalendarControllerDocs { + + @Operation( + summary = "구글 캘린더 연동", + description = """ + 구글 OAuth 동의 완료 후 발급된 authorizationCode로 구글 토큰을 교환하여 캘린더 연동 + - 가입 시 사용한 구글 계정과 다른 계정으로 연동을 시도하면 거부됨 + """, + security = @SecurityRequirement(name = "bearerAuth") + ) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "연동 성공", useReturnTypeSchema = true), + @ApiResponse(responseCode = "400", description = "authorizationCode 누락", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDto.class))), + @ApiResponse(responseCode = "401", description = "구글 인증 실패, 토큰 없음/만료, 또는 가입 이메일과 다른 계정으로 연동 시도", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDto.class))), + @ApiResponse(responseCode = "409", description = "이미 캘린더가 연동된 상태", + 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> connectCalendar( + @Parameter(hidden = true) CustomUserDetails userDetails, + @Valid @RequestBody CalendarConnectRequest request + ); + + @Operation( + summary = "구글 캘린더 연동 해제", + description = """ + 연동된 구글 캘린더 정보를 삭제하고 구글 토큰을 revoke + """, + security = @SecurityRequirement(name = "bearerAuth") + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "연동 해제 성공", useReturnTypeSchema = true), + @ApiResponse(responseCode = "401", description = "토큰 없음/만료/유효하지 않음", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDto.class))), + @ApiResponse(responseCode = "404", description = "연동된 캘린더가 없는 경우", + 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> disconnectCalendar( + @Parameter(hidden = true) CustomUserDetails userDetails + ); +} \ No newline at end of file From 7d16e4d093668114eb63c51412ff6ba0955d5647 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 05:45:45 +0900 Subject: [PATCH 15/19] =?UTF-8?q?=20feat:=20=EA=B5=AC=EA=B8=80=20=EC=BA=98?= =?UTF-8?q?=EB=A6=B0=EB=8D=94=20=EC=97=B0=EB=8F=99/=ED=95=B4=EC=A0=9C=20AP?= =?UTF-8?q?I=20=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CalendarController.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/java/com/Timo/Timo/domain/calendar/controller/CalendarController.java diff --git a/src/main/java/com/Timo/Timo/domain/calendar/controller/CalendarController.java b/src/main/java/com/Timo/Timo/domain/calendar/controller/CalendarController.java new file mode 100644 index 00000000..3172d3c1 --- /dev/null +++ b/src/main/java/com/Timo/Timo/domain/calendar/controller/CalendarController.java @@ -0,0 +1,53 @@ +package com.Timo.Timo.domain.calendar.controller; + +import com.Timo.Timo.domain.calendar.docs.CalendarControllerDocs; +import com.Timo.Timo.domain.calendar.dto.request.CalendarConnectRequest; +import com.Timo.Timo.domain.calendar.dto.response.CalendarConnectResponse; +import com.Timo.Timo.domain.calendar.dto.response.CalendarDisconnectResponse; +import com.Timo.Timo.domain.calendar.factory.CalendarResponseFactory; +import com.Timo.Timo.domain.calendar.service.CalendarService; +import com.Timo.Timo.global.auth.principal.CustomUserDetails; +import com.Timo.Timo.global.response.BaseResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/users/calendar") +@RequiredArgsConstructor +@Tag(name = "Calendar", description = "구글 캘린더 연동 API") +public class CalendarController implements CalendarControllerDocs { + + private final CalendarService calendarService; + private final CalendarResponseFactory calendarResponseFactory; + + @Override + @PostMapping + public ResponseEntity> connectCalendar( + @AuthenticationPrincipal CustomUserDetails userDetails, + @Valid @RequestBody CalendarConnectRequest request + ) { + Long userId = userDetails.getUserId(); + CalendarConnectResponse response = calendarService.connect(userId, request); + + return calendarResponseFactory.connectResponse(response); + } + + @Override + @DeleteMapping + public ResponseEntity> disconnectCalendar( + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + Long userId = userDetails.getUserId(); + CalendarDisconnectResponse response = calendarService.disconnect(userId); + + return calendarResponseFactory.disconnectResponse(response); + } +} From 07282c5ce554eec556216f7f8f10e5942b2707d0 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 05:56:01 +0900 Subject: [PATCH 16/19] =?UTF-8?q?fix:=20user=20=EC=97=B0=EA=B4=80=EA=B4=80?= =?UTF-8?q?=EA=B3=84=20=EB=A7=A4=ED=95=91=EC=9D=84=20@JoinColumn=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Timo/Timo/domain/calendar/entity/CalendarConnection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java index e6afc6ca..d148506d 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/entity/CalendarConnection.java @@ -6,6 +6,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import java.time.LocalDateTime; @@ -26,7 +27,7 @@ public class CalendarConnection { private Long id; @OneToOne - @Column(name = "user_id", nullable = false, unique = true) + @JoinColumn(name = "user_id", nullable = false, unique = true) private User user; @Column(name = "calendar_email", nullable = false) From db14d2f41f60e4d4d73742ca3d78592a52e184b5 Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 06:06:11 +0900 Subject: [PATCH 17/19] =?UTF-8?q?docs:=20Swagger=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Timo/domain/calendar/docs/CalendarControllerDocs.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java b/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java index a44037c8..d87d50b9 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/docs/CalendarControllerDocs.java @@ -22,8 +22,8 @@ public interface CalendarControllerDocs { @Operation( summary = "구글 캘린더 연동", description = """ - 구글 OAuth 동의 완료 후 발급된 authorizationCode로 구글 토큰을 교환하여 캘린더 연동 - - 가입 시 사용한 구글 계정과 다른 계정으로 연동을 시도하면 거부됨 + 구글 OAuth 동의 완료 후 발급된 authorizationCode로 구글 토큰을 교환하여 캘린더 연동합니다. + 가입 시 사용한 구글 계정과 다른 계정으로 연동을 시도하면 거부됩니다. """, security = @SecurityRequirement(name = "bearerAuth") ) @@ -46,7 +46,7 @@ ResponseEntity> connectCalendar( @Operation( summary = "구글 캘린더 연동 해제", description = """ - 연동된 구글 캘린더 정보를 삭제하고 구글 토큰을 revoke + 연동된 구글 캘린더 정보를 삭제하고 구글 토큰을 revoke합니다. """, security = @SecurityRequirement(name = "bearerAuth") ) From 69ce4a53c00e598d102a9a91ddbfea4a7efd119f Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 06:11:36 +0900 Subject: [PATCH 18/19] =?UTF-8?q?docs:=20Swagger=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Timo/global/auth/docs/AuthControllerDocs.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/Timo/Timo/global/auth/docs/AuthControllerDocs.java b/src/main/java/com/Timo/Timo/global/auth/docs/AuthControllerDocs.java index dcb4fad6..a0ad0584 100644 --- a/src/main/java/com/Timo/Timo/global/auth/docs/AuthControllerDocs.java +++ b/src/main/java/com/Timo/Timo/global/auth/docs/AuthControllerDocs.java @@ -24,11 +24,11 @@ public interface AuthControllerDocs { @Operation( summary = "AccessToken 발급", description = """ - 1회성 인증 코드(code)를 AccessToken으로 교환 - - isNewUser: 이번 요청으로 신규 회원가입이 이루어졌는지 여부 (true면 아직 온보딩을 진행한 적이 없는 사용자) - - user.onboardingCompleted: 온보딩 완료 여부 (false면 온보딩 화면으로, true면 홈 화면으로 이동) - - 기존 가입자가 온보딩을 중단한 경우에도 isNewUser는 false, onboardingCompleted는 false로 반환될 수 가능 - - RefreshToken과 sessionId는 Set-Cookie 헤더로 전달되며, 응답 바디에는 포함되지 않음 + 1회성 인증 코드(code)를 AccessToken으로 교환합니다. + - isNewUser: 이번 요청으로 신규 회원가입이 이루어졌는지 여부 (true면 아직 온보딩을 진행한 적이 없는 사용자) + - user.onboardingCompleted: 온보딩 완료 여부 (false면 온보딩 화면으로, true면 홈 화면으로 이동) + - 기존 가입자가 온보딩을 중단한 경우에도 isNewUser는 false, onboardingCompleted는 false로 반환될 수 가능 + - RefreshToken과 sessionId는 Set-Cookie 헤더로 전달되며, 응답 바디에는 포함되지 않음 """ ) @SecurityRequirements @@ -93,8 +93,8 @@ ResponseEntity> token( @Operation( summary = "AccessToken 재발급", description = """ - 쿠키로 전달된 RefreshToken과 sessionId를 검증하여 AccessToken 재발급 - 재발급 성공 시 RefreshToken과 sessionId 쿠키 갱신 + 쿠키로 전달된 RefreshToken과 sessionId를 검증하여 AccessToken 재발급합니다. + 재발급 성공 시 RefreshToken과 sessionId 쿠키를 갱신합니다. """ ) From eb7da00d6f76ca18e6e9ee12b79b2dec58cadb4d Mon Sep 17 00:00:00 2001 From: jy000n Date: Wed, 8 Jul 2026 06:19:16 +0900 Subject: [PATCH 19/19] =?UTF-8?q?fix:=20CalendarConnectResponse=20connecte?= =?UTF-8?q?dAt=20=EC=9D=91=EB=8B=B5=20=ED=8F=AC=EB=A7=B7=EC=9D=84=20yyyy-M?= =?UTF-8?q?M-dd=20HH:mm:ss=EB=A1=9C=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calendar/dto/response/CalendarConnectResponse.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java index ab847ae9..06e0dc14 100644 --- a/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java +++ b/src/main/java/com/Timo/Timo/domain/calendar/dto/response/CalendarConnectResponse.java @@ -1,5 +1,7 @@ package com.Timo.Timo.domain.calendar.dto.response; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDateTime; import lombok.Builder; @@ -7,5 +9,8 @@ public record CalendarConnectResponse( boolean calendarConnected, String calendarEmail, + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Schema(example = "2026-07-06 17:51:50", type = "string") LocalDateTime connectedAt ) {}