Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4ea201f
feat: user, profile, metadata 테스트코드 (#121)
cba700 Jul 28, 2025
8636a8b
fix: 랭킹 업데이트 24시간마다 -> 10분마다 (#123)
cba700 Jul 28, 2025
12f7f9d
feat: 회원탈퇴 기능 구현 (#107)
cba700 Jul 28, 2025
fd730b2
feat: 유저쿠폰 테스트코드작성 (#120)
taeho4523 Jul 29, 2025
888a618
refactor: 불변객체 record사용 (#128)
taeho4523 Jul 29, 2025
6013675
feat : 스케쥴러 추가, adminCoupon&teacherLesson 테스트코드 작성
iamjieunkim Jul 29, 2025
c461877
fix: 레슨 생성시 로직 순서변경
iamjieunkim Jul 29, 2025
f6260f7
feat: 검색 성능개선 및 누락된 검색 조건, 필터링 추가 (#133)
Ji-minhyeok Jul 29, 2025
0841f8a
refactor: 댓글, 리뷰 리펙토링 (#124)
ense333 Jul 29, 2025
8842427
refactor:쿠폰검증조건추가
iamjieunkim Jul 29, 2025
8ce63f0
fix: users 테이블 -> user로 변경
ense333 Jul 29, 2025
2eef7b4
refactor: 회원관련 수정 (#134)
cba700 Jul 29, 2025
acce190
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
ense333 Jul 29, 2025
4873f76
feat: 메타데이터 스케줄링 도입 (#140)
cba700 Jul 29, 2025
cc15b96
fix : 랭킹 집계 기준 변경 , 평점 소수점 자르기 (#146)
cba700 Jul 29, 2025
619a0b3
feat: 레슨 신청 결제 대기 상태 도입 (#148)
Ji-minhyeok Jul 29, 2025
cc56b0f
fix: adminLessonServiceTest오류수정
iamjieunkim Jul 29, 2025
254019f
feat: 프로메테우스,그라파나 세팅 (#141)
taeho4523 Jul 29, 2025
1deac85
refactor : lessonParticipantStatus추가에 따른 로직 수정
iamjieunkim Jul 30, 2025
2914a93
feat 레슨 제외 페이징 구조 통일 및 리뷰, 댓글 관련 서비스 로직 수정 (#153)
ense333 Jul 30, 2025
87015b0
feat: 삭제 로직 변경 (#154)
ense333 Jul 30, 2025
42c288c
feat: S3 업로드, 조회 로직 구현 (#158)
Ji-minhyeok Jul 30, 2025
a370915
feat: base response 형식 적용 (#161)
Ji-minhyeok Jul 30, 2025
e264891
refactor:openTime추가
iamjieunkim Jul 30, 2025
8289518
fix : 랭킹 카테고리 조회 메타데이터 사용 , 랭킹 캐싱 ttl 11분으로 교체 (#159)
cba700 Jul 30, 2025
3f2bcf3
refactor: 테스트코드 빌더삭제 (#147)
taeho4523 Jul 30, 2025
da722b1
refactor: 페이징 관련 구조 통합
ense333 Jul 30, 2025
0881001
feat: 선착순 레슨 신청시 openTime 비교 예외처리 (#164)
Ji-minhyeok Jul 30, 2025
465cf0f
fix: entity 컬럼 변경 (#166)
ense333 Jul 30, 2025
007028d
refactor:이미지 길이 수정
iamjieunkim Jul 30, 2025
a8fbd00
hotfix: 컬럼 길이 제한 에러 발생 hot fix
Ji-minhyeok Jul 30, 2025
3466e76
feat: tossPay 엔티티 컬럼 수정 (#170)
ense333 Jul 31, 2025
f475dc5
refactor: 쿼리 관련 리팩토링 (#172)
ense333 Aug 30, 2025
d719ad9
Refactor/171/toss query fix (#173)
ense333 Aug 30, 2025
2a5bd10
feat: 동시성 테스트를 위한 api 생성 (#182)
Ji-minhyeok Nov 24, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ out/
.vscode/

application.yml
application-test.yml
.env
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
implementation 'io.awspring.cloud:spring-cloud-starter-aws-secrets-manager-config:2.4.4'
implementation 'com.opencsv:opencsv:5.11.2'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand Down
27 changes: 27 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ services:
networks:
- app-tier

prometheus:
user: "root"
image: prom/prometheus
container_name: prometheus_container
volumes:
- /home/ubuntu/WEB5_7_3star_BE/prometheus-grafana/prometheus/config:/etc/prometheus
- /home/ubuntu/WEB5_7_3star_BE/prometheus-grafana/prometheus/volume:/prometheus/data
ports:
- 9090:9090
command:
- '--config.file=/etc/prometheus/prometheus.yml'
restart: always
networks:
- app-tier

grafana:
user: "root"
image: grafana/grafana
container_name: grafana_container
ports:
- 3000:3000
volumes:
- ./grafana/volume:/var/lib/grafana
restart: always
networks:
- app-tier

volumes:
mysqldb-data:
driver: local
Expand Down
26 changes: 26 additions & 0 deletions prometheus-grafana/prometheus/config/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
global:
scrape_interval: 15s
scrape_timeout: 15s
evaluation_interval: 2m
external_labels:
monitor: 'monitor'
query_log_file: query_log_file.log
rule_files:
- "rule.yml"
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 10s
scrape_timeout: 10s
metrics_path: '/metrics'
honor_labels: false
honor_timestamps: false
scheme: 'http'
static_configs:
- targets: [ '43.202.206.47:9090' ]
# - targets: [ 'localhost:9090' ]
labels:
service: 'monitor-1'
- job_name: 'node'
static_configs:
# - targets: [ 'localhost:9100' ]
- targets: [ '43.202.206.47:9100' ]
17 changes: 17 additions & 0 deletions prometheus-grafana/prometheus/config/rule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
groups:
- name: rule
rules:
- alert: InstanceDown
expr: up == 0
for: 5m
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
- alert: APIHighRequestLatency
expr: api_http_request_latencies_second{quantile="0.5"} > 1
for: 10m
annotations:
summary: "High request latency on {{ $labels.instance }}"
description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"
12 changes: 10 additions & 2 deletions src/main/java/com/threestar/trainus/TrainUsApplication.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package com.threestar.trainus;

import java.util.TimeZone;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.context.annotation.Bean;

@EnableJpaAuditing
@SpringBootApplication
public class TrainUsApplication {

public static void main(String[] args) {
SpringApplication.run(TrainUsApplication.class, args);
}

@Bean
public CommandLineRunner init() {
return args -> {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
};
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
package com.threestar.trainus.domain.comment.controller;

import org.springframework.beans.factory.annotation.Value;
import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.threestar.trainus.domain.comment.dto.CommentCreateRequestDto;
import com.threestar.trainus.domain.comment.dto.CommentPageResponseDto;
import com.threestar.trainus.domain.comment.dto.CommentPageWrapperDto;
import com.threestar.trainus.domain.comment.dto.CommentResponseDto;
import com.threestar.trainus.domain.comment.mapper.CommentMapper;
import com.threestar.trainus.domain.comment.service.CommentService;
import com.threestar.trainus.global.annotation.LoginUser;
import com.threestar.trainus.global.dto.PageRequestDto;
import com.threestar.trainus.global.unit.BaseResponse;
import com.threestar.trainus.global.unit.PagedResponse;

Expand All @@ -34,8 +34,6 @@
public class CommentController {

private final CommentService commentService;
@Value("${spring.page.size.limit}")
private int pageSizeLimit;

@PostMapping("/{lessonId}")
@Operation(summary = "댓글 작성", description = "레슨 ID에 해당되는 댓글을 작성합니다.")
Expand All @@ -47,14 +45,11 @@ public ResponseEntity<BaseResponse<CommentResponseDto>> createComment(@PathVaria

@GetMapping("/{lessonId}")
@Operation(summary = "댓글 조회", description = "레슨 ID에 해당되는 댓글들을 조회합니다.")
public ResponseEntity<PagedResponse<CommentPageWrapperDto>> readAll(@PathVariable Long lessonId,
@RequestParam("page") int page,
@RequestParam("pageSize") int pageSize) {
int correctPage = Math.max(page, 1);
int correctPageSize = Math.max(1, Math.min(pageSize, pageSizeLimit));
CommentPageResponseDto commentsInfo = commentService.readAll(lessonId, correctPage, correctPageSize);
CommentPageWrapperDto comments = CommentMapper.toCommentPageWrapperDto(commentsInfo);
return PagedResponse.ok("댓글 조회 성공", comments, commentsInfo.getCount(), HttpStatus.OK);
public ResponseEntity<PagedResponse<List<CommentResponseDto>>> readAll(@PathVariable Long lessonId,
@Valid @ModelAttribute PageRequestDto pageRequestDto) {
CommentPageResponseDto commentsInfo = commentService.readAll(lessonId, pageRequestDto.getPage(),
pageRequestDto.getLimit());
return PagedResponse.ok("댓글 조회 성공", commentsInfo.comments(), commentsInfo.count(), HttpStatus.OK);
}

@DeleteMapping("/{commentId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;

@Getter
public class CommentCreateRequestDto {
public record CommentCreateRequestDto(
@NotBlank(message = "댓글 내용은 필수입니다")
@Size(max = 255, message = "댓글은 255자 이내여야 합니다.")
private String content;
private Long parentCommentId;
String content,
Long parentCommentId
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import java.util.List;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CommentPageResponseDto {

private List<CommentResponseDto> comments;
private Integer count;
public record CommentPageResponseDto(
List<CommentResponseDto> comments,
Integer count
) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import java.time.LocalDateTime;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class CommentResponseDto {
private Long commentId;
private Long userId;
private String content;
private Long parentCommentId;
private Boolean deleted;
private LocalDateTime createdAt;
public record CommentResponseDto(
Long commentId,
Long userId,
String nickname,
String content,
Long parentCommentId,
Boolean deleted,
LocalDateTime createdAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.threestar.trainus.domain.comment.dto;

import java.time.LocalDateTime;

public interface CommentWithUserProjection {
Long getCommentId();

String getContent();

Long getParentCommentId();

Boolean getDeleted();

LocalDateTime getCreatedAt();

Long getUserId();

String getNickname();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import java.util.List;

import com.threestar.trainus.domain.comment.dto.CommentPageResponseDto;
import com.threestar.trainus.domain.comment.dto.CommentPageWrapperDto;
import com.threestar.trainus.domain.comment.dto.CommentResponseDto;
import com.threestar.trainus.domain.comment.dto.CommentWithUserProjection;
import com.threestar.trainus.domain.comment.entity.Comment;

public class CommentMapper {
Expand All @@ -20,6 +20,19 @@ public static CommentResponseDto toCommentResponseDto(Comment comment) {
.parentCommentId(comment.getParentCommentId())
.deleted(comment.getDeleted())
.createdAt(comment.getCreatedAt())
.nickname(comment.getUser().getNickname())
.build();
}

public static CommentResponseDto toCommentResponseDtoWithProjection(CommentWithUserProjection comment) {
return CommentResponseDto.builder()
.commentId(comment.getCommentId())
.userId(comment.getUserId())
.content(comment.getContent())
.parentCommentId(comment.getParentCommentId())
.deleted(comment.getDeleted())
.createdAt(comment.getCreatedAt())
.nickname(comment.getNickname())
.build();
}

Expand All @@ -29,10 +42,4 @@ public static CommentPageResponseDto toCommentPageResponseDto(List<CommentRespon
.count(count)
.build();
}

public static CommentPageWrapperDto toCommentPageWrapperDto(CommentPageResponseDto commentPageResponseDto) {
return CommentPageWrapperDto.builder()
.comments(commentPageResponseDto.getComments())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,54 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.threestar.trainus.domain.comment.dto.CommentWithUserProjection;
import com.threestar.trainus.domain.comment.entity.Comment;

public interface CommentRepository extends JpaRepository<Comment, Long> {

//create index idx_lesson_id_parent_comment_id_comment_id on comments(lesson_id, parent_comment_id asc, comment_id asc); 인덱스 통해 조회 성능 최적화
@Query(value = """
select comments.comment_id, comments.lesson_id, comments.user_id, comments.content,
comments.parent_comment_id, comments.deleted, comments.created_at, comments.updated_at
from ( select comment_id from comments where lesson_id = :lessonId order by parent_comment_id asc, comment_id asc limit :limit offset :offset
) t left join comments on t.comment_id = comments.comment_id
SELECT
c.comment_id AS commentId,
c.content AS content,
c.parent_comment_id AS parentCommentId,
c.deleted AS deleted,
c.created_at AS createdAt,
u.id AS userId,
u.nickname AS nickname
FROM (
SELECT comment_id
FROM comments c
JOIN user u ON c.user_id = u.id AND u.deleted_at IS NULL
WHERE lesson_id = :lessonId
ORDER BY parent_comment_id ASC, comment_id ASC
LIMIT :limit OFFSET :offset
) t
JOIN comments c ON t.comment_id = c.comment_id
JOIN user u ON c.user_id = u.id
""", nativeQuery = true)
List<Comment> findAll(@Param("lessonId") Long lessonId, @Param("offset") int offset, @Param("limit") int limit);
List<CommentWithUserProjection> findAll(@Param("lessonId") Long lessonId, @Param("offset") int offset,
@Param("limit") int limit);

@Query(value = """
select count(*) from ( select comment_id from comments where lesson_id = :lessonId limit :limit) t
SELECT count(*) FROM (
SELECT comment_id
FROM comments c
JOIN user u ON c.user_id = u.id AND u.deleted_at IS NULL
WHERE lesson_id = :lessonId
LIMIT :limit
) t
""", nativeQuery = true)
Integer count(@Param("lessonId") Long lessonId, @Param("limit") int limit);

@Query(value = """
select count(*) from (select comment_id from comments where lesson_id = :lessonId and parent_comment_id = :parentCommentId limit :limit) t
SELECT count(*) FROM (
SELECT comment_id
FROM comments c
JOIN user u ON c.user_id = u.id AND u.deleted_at IS NULL
WHERE lesson_id = :lessonId AND parent_comment_id = :parentCommentId
LIMIT :limit
) t
""", nativeQuery = true)
Long countBy(@Param("lessonId") Long lessonId, @Param("parentCommentId") Long parentCommentId,
@Param("limit") int limit);
Expand Down
Loading