Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b2cb122
feat: 로그인 리다이랙트 구현
Jun 7, 2026
127687c
[Fix] 이해도 체크 선택 상태 응답 추가
issuejong Jun 7, 2026
0f9bf96
커리큘럼 도메인 필수 필드 검증 추가
xihxxn Jun 7, 2026
3b4e84f
Merge pull request #177 from pirogramming/#82
xihxxn Jun 7, 2026
886411d
[Refactor] json.isSuccess 에러 처리 추가
issuejong Jun 7, 2026
3231156
fix: 질문/댓글 이미지 단독 업로드 불가 문제 수정
kkw610 Jun 7, 2026
546f3f2
Merge pull request #176 from pirogramming/fix/#174
issuejong Jun 7, 2026
1693934
feat: 구글 검색 로고 수정
Jun 7, 2026
f403376
Merge pull request #178 from pirogramming/fix/#175
kkw610 Jun 7, 2026
436a43f
[Validation] 커리큘럼 폼 필수 필드 프론트 검증 추가
xihxxn Jun 7, 2026
338ffdf
[feat] qna 반응형 구현
kdhye1119 Jun 7, 2026
95e7721
[Style] 커리큘럼 폼 필수 필드 * 표시 추가
xihxxn Jun 7, 2026
1c5e970
Merge pull request #179 from pirogramming/feat/172
lilyyang0077 Jun 7, 2026
752b3c2
Merge pull request #182 from pirogramming/Feat/#173
kdhye1119 Jun 7, 2026
3e3c41c
Merge pull request #180 from pirogramming/#82
xihxxn Jun 7, 2026
99172cc
fix: 질문/댓글 이미지 단독 업로드 시 텍스트 없어도 제출 가능하도록 프론트 수정
kkw610 Jun 7, 2026
96086cd
Merge pull request #183 from pirogramming/fix/#181
kkw610 Jun 7, 2026
f506b12
커리큘럼 도메인 필수 필드 검증 추가
xihxxn Jun 7, 2026
a711043
[Fix] 이해도 체크 선택 상태 응답 추가
issuejong Jun 7, 2026
6adf55e
[Refactor] json.isSuccess 에러 처리 추가
issuejong Jun 7, 2026
a0fda66
fix: 질문/댓글 이미지 단독 업로드 불가 문제 수정
kkw610 Jun 7, 2026
9c3b5a0
feat: 로그인 리다이랙트 구현
Jun 7, 2026
ea7202c
feat: 구글 검색 로고 수정
Jun 7, 2026
c66ad89
[feat] qna 반응형 구현
kdhye1119 Jun 7, 2026
e0e7deb
[Validation] 커리큘럼 폼 필수 필드 프론트 검증 추가
xihxxn Jun 7, 2026
9c163e4
[Style] 커리큘럼 폼 필수 필드 * 표시 추가
xihxxn Jun 7, 2026
bab5bae
fix: 질문/댓글 이미지 단독 업로드 시 텍스트 없어도 제출 가능하도록 프론트 수정
kkw610 Jun 7, 2026
bc96873
[Fix] 커리큘럼 요일 매핑 화목토 → 전체 요일로 확장
xihxxn Jun 7, 2026
4107679
[Feat] 커리큘럼 0주차 세션 생성 추가
xihxxn Jun 7, 2026
202e51f
Merge pull request #184 from pirogramming/#82
xihxxn Jun 7, 2026
ec5e954
[Feat] 세션 상태별 커리큘럼 콘텐츠 노출 분기 처리
xihxxn Jun 7, 2026
92b515e
[Feat] 프론트 SSE 구독 유틸 추가
issuejong Jun 7, 2026
52ac748
[Feat] 질문 메인 페이지 SSE 연결
issuejong Jun 7, 2026
5677e69
[Fix] role 상태를 useState로 이동하여 첫 렌더링 시 권한 반영
xihxxn Jun 7, 2026
3440642
Merge pull request #186 from pirogramming/#82
xihxxn Jun 7, 2026
a04f9eb
[Feat] 질문 메인 댓글 실시간 반영
issuejong Jun 7, 2026
bfd0794
[Feat] 질문 메인 이해도 실시간 반영
issuejong Jun 7, 2026
9faf6bf
[Feat] 질문 상세 페이지 댓글 실시간 반영
issuejong Jun 7, 2026
c8131db
[Feat] 질문 상태 변경 SSE 이벤트 반영
issuejong Jun 7, 2026
8fa90ce
[Feat] 댓글 수정 삭제 SSE 이벤트 반영
issuejong Jun 7, 2026
ff621c4
[Fix] SSE 연결 프록시 우회 및 버퍼링 방지
issuejong Jun 7, 2026
23adcc5
Merge pull request #188 from pirogramming/feat/#185
issuejong Jun 7, 2026
c889831
feat: 커리큘럼 수정/생성 버튼 안 뜸 문제 해결
Jun 7, 2026
5c3b054
Merge pull request #190 from pirogramming/feat/189
lilyyang0077 Jun 7, 2026
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
3 changes: 3 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ dependencies {
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-database-postgresql'

// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'

// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.example.Piroin.project.domain.curriculum.dto.CurriculumReqDTO;
import com.example.Piroin.project.domain.curriculum.dto.CurriculumResDTO;
import com.example.Piroin.project.domain.curriculum.service.CurriculumService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -26,14 +27,14 @@ public ResponseEntity<List<CurriculumResDTO.CreateDayRes>> getAllDays() {

@PostMapping
public ResponseEntity<CurriculumResDTO.CreateDayRes> createDay(
@RequestBody CurriculumReqDTO.CreateDayReq req) {
@RequestBody @Valid CurriculumReqDTO.CreateDayReq req) {
return ResponseEntity.status(HttpStatus.CREATED).body(curriculumService.createDay(req));
}

@PatchMapping("/{sessionDate}")
public ResponseEntity<CurriculumResDTO.CreateDayRes> updateDay(
@PathVariable LocalDate sessionDate,
@RequestBody CurriculumReqDTO.UpdateDayReq req) {
@RequestBody @Valid CurriculumReqDTO.UpdateDayReq req) {
return ResponseEntity.ok(curriculumService.updateDay(sessionDate, req));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.example.Piroin.project.domain.curriculum.enums.SessionDayPart;
import com.example.Piroin.project.domain.curriculum.enums.SessionStatus;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -13,18 +16,32 @@ public class CurriculumReqDTO {
@Getter
@NoArgsConstructor
public static class CreateDayReq {
@NotNull(message = "기수를 입력해주세요.")
private Integer generation;

@NotNull(message = "주차를 입력해주세요.")
private Long week;

@NotNull(message = "세션 날짜를 입력해주세요.")
private LocalDate sessionDate;

@NotEmpty(message = "세션 목록을 입력해주세요.")
@Valid
private List<SessionReq> sessions;
}

@Getter
@NoArgsConstructor
public static class SessionReq {
@NotNull(message = "세션 시간대를 입력해주세요.")
private SessionDayPart dayPart;

@NotNull(message = "세션 제목을 입력해주세요.")
private String title;

@NotNull(message = "발표자를 입력해주세요.")
private String hostName;

private String sessionMaterialUrl;
private String sessionMaterialName;
private String recordingUrl;
Expand All @@ -37,16 +54,25 @@ public static class SessionReq {
@Getter
@NoArgsConstructor
public static class UpdateDayReq {
@NotNull(message = "기수를 입력해주세요.")
private Integer generation;

@NotNull(message = "주차를 입력해주세요.")
private Long week;

private LocalDate newSessionDate;

@NotEmpty(message = "세션 목록을 입력해주세요.")
@Valid
private List<UpdateSessionItemReq> sessions;
}

@Getter
@NoArgsConstructor
public static class UpdateSessionItemReq {
@NotNull(message = "세션 시간대를 입력해주세요.")
private SessionDayPart dayPart;

private SessionStatus status;
private String title;
private String hostName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ public ResponseEntity<ApiResponse<QuestionResDTO.QuestionRoomResponse>> getQuest
// text/event-stream으로 연결을 유지하며, 댓글 생성 같은 목록 갱신 이벤트를 받는다.
// 인증 헤더가 필요하므로 프론트에서는 기본 EventSource 대신 fetch 기반 SSE 클라이언트로 구독한다.
@GetMapping(value = "/api/sessions/{sessionId}/questions/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribeQuestionEvents(@PathVariable Long sessionId) {
return questionService.subscribeQuestionEvents(sessionId);
public ResponseEntity<SseEmitter> subscribeQuestionEvents(@PathVariable Long sessionId) {
return ResponseEntity.ok()
.contentType(MediaType.TEXT_EVENT_STREAM)
.header("Cache-Control", "no-cache")
.header("X-Accel-Buffering", "no")
.body(questionService.subscribeQuestionEvents(sessionId));
}

// 질문 상세 조회
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ public record UnderstandingCheckResponse(
Integer understoodCount,
// 오른쪽 X 뱃지 숫자
Integer notUnderstoodCount,
// 현재 로그인 유저가 누른 선택지. 누르지 않았거나 취소한 상태면 null
UnderstandResChoice selectedChoice,
LocalDateTime createdAt
) {
}
Expand Down Expand Up @@ -195,6 +197,17 @@ public record CommentCreatedEvent(
) {
}

// 댓글 수정/삭제 시 SSE로 내려가는 목록 갱신 이벤트 응답
public record CommentUpdatedEvent(
String type,
Long sessionId,
Long questionId,
Boolean isResolved,
Integer commentCount,
List<PreviewCommentResponse> previewComments
) {
}

// O/X 클릭 직후 응답. selectedChoice가 null이면 같은 선택지를 다시 눌러 취소된 상태다.
public record UnderstandingResponseResult(
Long checkId,
Expand Down Expand Up @@ -241,6 +254,19 @@ public record QuestionCreatedEvent(
) {
}

// 좋아요, 해결 상태, 본문 수정, 삭제처럼 기존 질문의 상태가 바뀔 때 내려가는 SSE 이벤트
public record QuestionUpdatedEvent(
String type,
Long sessionId,
Long questionId,
String content,
Boolean isResolved,
Integer likeCount,
Boolean isDeleted,
LocalDateTime updatedAt
) {
}

// 운영진이 이해도 체크를 생성했을 때 SSE로 내려가는 이벤트.
// 같은 세션 질문방을 보고 있는 모든 클라이언트에게 전파된다.
public record UnderstandingCheckCreatedEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class Question {
@JoinColumn(name = "user_id", nullable = false)
private User user;

@Column(nullable = false, columnDefinition = "TEXT")
@Column(columnDefinition = "TEXT")
private String content;

@Column(name = "image_url", columnDefinition = "TEXT")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class QuestionComment {
@JoinColumn(name = "parent_comment_id")
private QuestionComment parentComment;

@Column(nullable = false, columnDefinition = "TEXT")
@Column(columnDefinition = "TEXT")
private String content;

@Column(name = "image_url", columnDefinition = "TEXT")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,21 @@ public void publishCommentCreated(Long sessionId, QuestionResDTO.CommentCreatedE
broadcast(sessionId, "comment-created", event);
}

// 댓글 수정/삭제 이벤트를 같은 세션 질문방을 구독 중인 모든 클라이언트에게 전파한다.
public void publishCommentUpdated(Long sessionId, QuestionResDTO.CommentUpdatedEvent event) {
broadcast(sessionId, "comment-updated", event);
}

// 질문 등록 이벤트를 같은 세션 질문방을 구독 중인 모든 클라이언트에게 전파한다.
public void publishQuestionCreated(Long sessionId, QuestionResDTO.QuestionCreatedEvent event) {
broadcast(sessionId, "question-created", event);
}

// 질문 상태 변경 이벤트를 같은 세션 질문방을 구독 중인 모든 클라이언트에게 전파한다.
public void publishQuestionUpdated(Long sessionId, QuestionResDTO.QuestionUpdatedEvent event) {
broadcast(sessionId, "question-updated", event);
}

// 이해도 체크 생성 이벤트를 같은 세션 질문방을 구독 중인 모든 클라이언트에게 전파한다.
public void publishUnderstandingCheckCreated(Long sessionId, QuestionResDTO.UnderstandingCheckCreatedEvent event) {
broadcast(sessionId, "understanding-check-created", event);
Expand Down
Loading
Loading