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
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @yongjun0511 @Ssamssamukja @2ghrms @juuuuone @dev2yup
* @Ssamssamukja @2ghrms @juuuuone @dev2yup
3 changes: 2 additions & 1 deletion clokey-api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ RUN gradle :clokey-api:build -x test --no-daemon

FROM eclipse-temurin:21-jdk-jammy
WORKDIR /app
ENV TZ=Asia/Seoul
COPY --from=builder /app/clokey-api/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
ENTRYPOINT ["java", "-Duser.timezone=Asia/Seoul", "-jar", "app.jar"]
1 change: 1 addition & 0 deletions clokey-api/dev-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: dev
TZ: Asia/Seoul

# Database
DEV_MYSQL_HOST: ${DEV_MYSQL_HOST}
Expand Down
1 change: 1 addition & 0 deletions clokey-api/prod-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: prod
TZ: Asia/Seoul

# Database
PROD_MYSQL_HOST: ${PROD_MYSQL_HOST}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
@Component
public class UniqueUtil {

private static final int MAX_NICKNAME_LENGTH = 20;
private static final String[] PREFIX_NAMES = {"미니멀한", "모던한", "케주얼한", "스트릿한"};

private static final String[] CLOTHING_CATEGORIES = {
Expand All @@ -22,9 +23,23 @@ public String generateRandomNickname() {

String prefix = PREFIX_NAMES[random.nextInt(PREFIX_NAMES.length)];
String category = CLOTHING_CATEGORIES[random.nextInt(CLOTHING_CATEGORIES.length)];
String nicknamePrefix = prefix + "_" + category + "_";

String uuidPart = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
int suffixLength = MAX_NICKNAME_LENGTH - nicknamePrefix.length();
if (suffixLength <= 0) {
return nicknamePrefix.substring(0, MAX_NICKNAME_LENGTH);
}

return prefix + "-" + category + "-" + uuidPart;
return nicknamePrefix + generateSuffix(suffixLength);
}

private String generateSuffix(int length) {
StringBuilder suffix = new StringBuilder(length);

while (suffix.length() < length) {
suffix.append(UUID.randomUUID().toString().replace("-", ""));
}

return suffix.substring(0, length);
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.clokey.domain.coordinate.service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
Expand Down Expand Up @@ -45,6 +46,8 @@
@Transactional(readOnly = true)
public class CoordinateServiceImpl implements CoordinateService {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final MemberUtil memberUtil;

private final CoordinateRepository coordinateRepository;
Expand Down Expand Up @@ -89,7 +92,7 @@ public CoordinateCreateResponse createDailyCoordinate(DailyCoordinateCreateReque
validateExceedingCoordinationClothesLimit(request.payloads());
validateDuplicatedClothes(clothes);
validateAllClothesOwnership(currentMember, clothes);
validateDailyCoordinateExist(currentMember.getId(), LocalDate.now());
validateDailyCoordinateExist(currentMember.getId(), LocalDate.now(KST));

Coordinate coordinate =
Coordinate.createDailyCoordinate(request.coordinateImageUrl(), currentMember);
Expand Down Expand Up @@ -522,7 +525,7 @@ private Coordinate getCoordinateById(Long coordinateId) {

private Coordinate getTodayDailyCoordinate(Member member) {
return coordinateRepository
.findDailyCoordinateByDateAndMemberId(LocalDate.now(), member.getId())
.findDailyCoordinateByDateAndMemberId(LocalDate.now(KST), member.getId())
.orElseThrow(
() ->
new BaseCustomException(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.clokey.domain.history.service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -48,6 +49,8 @@
@Transactional(readOnly = true)
public class HistoryServiceImpl implements HistoryService {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final MemberUtil memberUtil;

private final HistoryRepository historyRepository;
Expand Down Expand Up @@ -90,7 +93,7 @@ public HistoryCreateResponse createHistory(HistoryCreateRequest request) {
final String content =
Optional.ofNullable(request.content()).map(String::trim).orElse(null);
final History history =
History.createHistory(LocalDate.now(), content, currentMember, situation);
History.createHistory(LocalDate.now(KST), content, currentMember, situation);
historyRepository.save(history);

List<HistoryImage> images = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.clokey.domain.member.batch;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -17,13 +18,15 @@
@Slf4j
public class InactiveMemberDeletionBatch {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final MemberRepository memberRepository;
private final AuthService authService;

@Scheduled(cron = "0 0 0 * * *") // 매일 00:00:00
@Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") // 매일 00:00:00 KST
@Transactional
public void deleteInactiveMembers() {
LocalDateTime cutoffDate = LocalDateTime.now().minusDays(15);
LocalDateTime cutoffDate = LocalDateTime.now(KST).minusDays(15);
List<Member> inactiveMembers =
memberRepository.findInactiveMembersBefore(MemberStatus.INACTIVE, cutoffDate);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public record DuplicatedNicknameCheckRequest(
@NotBlank(message = "닉네임은 비워둘 수 없습니다.")
@Size(max = 20, message = "닉네임은 20자 이하여야 합니다.")
@Pattern(
regexp = "^[a-z가-힣._]+$",
message = "닉네임은 영어 소문자, 한글, 언더바(_), 점(.)만 허용됩니다.")
regexp = "^[a-z0-9가-힣._]+$",
message = "닉네임은 영어 소문자, 숫자, 한글, 언더바(_), 점(.)만 허용됩니다.")
@Schema(description = "중복을 확인할 닉네임", example = "clokey.홍길동")
String nickname) {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import org.clokey.member.enums.Visibility;

public record ProfileUpdateRequest(
@NotBlank(message = "닉네임은 비워둘 수 없습니다.") @Schema(description = "사용자의 닉네임", example = "juwon")
@NotBlank(message = "닉네임은 비워둘 수 없습니다.")
@Size(max = 20, message = "닉네임은 20자 이하여야 합니다.")
@Pattern(
regexp = "^[a-z0-9가-힣._]+$",
message = "닉네임은 영어 소문자, 숫자, 한글, 언더바(_), 점(.)만 허용됩니다.")
@Schema(description = "사용자의 닉네임", example = "juwon")
String nickname,
@Schema(description = "사용자의 한줄 소개", example = "한줄 소개")
@Size(max = 100, message = "바이오는 100자를 넘길 수 없습니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.clokey.domain.notification.dto.response.NotificationListResponse;
Expand All @@ -19,6 +20,8 @@
@RequiredArgsConstructor
public class CodiveNotificationRepositoryImpl implements CodiveNotificationRepositoryCustom {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final JPAQueryFactory queryFactory;

@Override
Expand Down Expand Up @@ -56,7 +59,7 @@ public void updateAllReadStatusByMemberId(Long memberId) {
queryFactory
.update(codiveNotification)
.set(codiveNotification.readStatus, ReadStatus.READ)
.set(codiveNotification.updatedAt, LocalDateTime.now())
.set(codiveNotification.updatedAt, LocalDateTime.now(KST))
.where(
codiveNotification.member.id.eq(memberId),
codiveNotification.readStatus.eq(ReadStatus.NOT_READ))
Expand Down
6 changes: 6 additions & 0 deletions clokey-api/src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
spring:
jackson:
time-zone: Asia/Seoul
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DEV_MYSQL_HOST}:${MYSQL_PORT}/${DB_NAME}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
Expand All @@ -12,6 +14,10 @@ spring:
hibernate:
ddl-auto: validate
open-in-view: false
properties:
hibernate:
jdbc:
time_zone: Asia/Seoul

flyway:
enabled: true
Expand Down
6 changes: 6 additions & 0 deletions clokey-api/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
spring:
jackson:
time-zone: Asia/Seoul
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${DB_NAME}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
Expand All @@ -12,6 +14,10 @@ spring:
hibernate:
ddl-auto: validate
open-in-view: false
properties:
hibernate:
jdbc:
time_zone: Asia/Seoul

flyway:
enabled: true
Expand Down
6 changes: 6 additions & 0 deletions clokey-api/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
spring:
jackson:
time-zone: Asia/Seoul
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${PROD_MYSQL_HOST}:${MYSQL_PORT}/${DB_NAME}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
Expand All @@ -12,6 +14,10 @@ spring:
hibernate:
ddl-auto: none
open-in-view: false
properties:
hibernate:
jdbc:
time_zone: Asia/Seoul

flyway:
enabled: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class 프로필_수정_요청_시 {
// given
ProfileUpdateRequest request =
new ProfileUpdateRequest(
"testNickname",
"testnickname",
"testBio",
Visibility.PUBLIC,
"https://img.example.com/bg.jpg");
Expand Down Expand Up @@ -135,7 +135,7 @@ class 프로필_수정_요청_시 {
String longBio = "a".repeat(101);
ProfileUpdateRequest request =
new ProfileUpdateRequest(
"testNickname",
"testnickname",
longBio,
Visibility.PRIVATE,
"https://img.example.com/bg.jpg");
Expand All @@ -154,6 +154,59 @@ class 프로필_수정_요청_시 {
.andExpect(jsonPath("$.message").value("잘못된 요청입니다."))
.andExpect(jsonPath("$.result.bio").value("바이오는 100자를 넘길 수 없습니다."));
}

@ParameterizedTest
@ValueSource(strings = {"clokey clokey", "CLOKEY", "clokey-user"})
void 닉네임_패턴을_위배하면_예외가_발생한다(String nickname) throws Exception {
// given
ProfileUpdateRequest request =
new ProfileUpdateRequest(
nickname,
"testBio",
Visibility.PRIVATE,
"https://img.example.com/bg.jpg");

// when
ResultActions perform =
mockMvc.perform(
patch("/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)));

// then
perform.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.isSuccess").value(false))
.andExpect(jsonPath("$.code").value("COMMON400"))
.andExpect(jsonPath("$.message").value("잘못된 요청입니다."))
.andExpect(
jsonPath("$.result.nickname")
.value("닉네임은 영어 소문자, 숫자, 한글, 언더바(_), 점(.)만 허용됩니다."));
}

@Test
void 닉네임이_20자를_초과하면_예외가_발생한다() throws Exception {
// given
ProfileUpdateRequest request =
new ProfileUpdateRequest(
"abcdefghijklmnopqrstu",
"testBio",
Visibility.PRIVATE,
"https://img.example.com/bg.jpg");

// when
ResultActions perform =
mockMvc.perform(
patch("/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)));

// then
perform.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.isSuccess").value(false))
.andExpect(jsonPath("$.code").value("COMMON400"))
.andExpect(jsonPath("$.message").value("잘못된 요청입니다."))
.andExpect(jsonPath("$.result.nickname").value("닉네임은 20자 이하여야 합니다."));
}
}

@Nested
Expand Down Expand Up @@ -227,10 +280,9 @@ class 아이디_중복확인_요청_시 {
.value(containsString("닉네임은 비워둘 수 없습니다.")));
}

// 허용 종류 : 영어 소문자, 한글, 언더바(_), 점(.)
// 허용 종류 : 영어 소문자, 숫자, 한글, 언더바(_), 점(.)
@ParameterizedTest
@ValueSource(
strings = {"clokey clokey", "CLOKEY", "clokey-user", "clokey,,user^^", "clokey1"})
@ValueSource(strings = {"clokey clokey", "CLOKEY", "clokey-user", "clokey,,user^^"})
void 닉네임_제약조건을_위배하면_예외가_발생한다(String nickname) throws Exception {
// given
DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(nickname);
Expand All @@ -249,7 +301,7 @@ class 아이디_중복확인_요청_시 {
.andExpect(jsonPath("$.message").value("잘못된 요청입니다."))
.andExpect(
jsonPath("$.result.nickname")
.value("닉네임은 영어 소문자, 한글, 언더바(_), 점(.)만 허용됩니다."));
.value("닉네임은 영어 소문자, 숫자, 한글, 언더바(_), 점(.)만 허용됩니다."));
}

@Test
Expand All @@ -274,7 +326,7 @@ class 아이디_중복확인_요청_시 {
}

@ParameterizedTest
@ValueSource(strings = {"clokey", "홍길동", "clokey.홍길동", "abc_def"})
@ValueSource(strings = {"clokey", "홍길동", "clokey.홍길동", "abc_def", "clokey1"})
void 닉네임_제약조건을_만족하면_중복_여부를_반환한다(String nickname) throws Exception {
// given
DuplicatedNicknameCheckRequest request = new DuplicatedNicknameCheckRequest(nickname);
Expand Down
Loading