From 572f64c4d3806a3ef6736308977a3b05970d06d0 Mon Sep 17 00:00:00 2001 From: ballpen Date: Mon, 14 Jul 2025 21:43:02 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20"git=20=EC=82=AC=EC=9A=A9=EB=B2=95?= =?UTF-8?q?=20=EA=B0=95=EC=9D=98"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/JoinUs/dp/DpApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/JoinUs/dp/DpApplication.java b/src/main/java/com/JoinUs/dp/DpApplication.java index 8045089..3bfc432 100644 --- a/src/main/java/com/JoinUs/dp/DpApplication.java +++ b/src/main/java/com/JoinUs/dp/DpApplication.java @@ -1,5 +1,7 @@ package com.JoinUs.dp; +// git 강의 + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; From 8c5d2ee98564c2d73da5916fd846d60928635b3f Mon Sep 17 00:00:00 2001 From: ballpen Date: Mon, 14 Jul 2025 21:46:51 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EA=B9=83=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=B2=95=20vscode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/JoinUs/dp/DpApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/JoinUs/dp/DpApplication.java b/src/main/java/com/JoinUs/dp/DpApplication.java index 3bfc432..46bf413 100644 --- a/src/main/java/com/JoinUs/dp/DpApplication.java +++ b/src/main/java/com/JoinUs/dp/DpApplication.java @@ -1,6 +1,6 @@ package com.JoinUs.dp; -// git 강의 +// git 강의 sdf import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; From c1ab841c04a7161084e015432948a0540af329ca Mon Sep 17 00:00:00 2001 From: kjinhub Date: Sat, 23 Aug 2025 19:39:05 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EB=8F=99=EC=95=84=EB=A6=AC=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EB=B0=8F=20=EA=B3=B5=EC=A7=80/FAQ=20API?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공통 모듈: ApiPath, Response, GlobalExceptionHandler, JwtProvider, SecurityConfiguration - 동아리 신청: ClubApplication 엔티티/DTO, Repository, Service, Controller - 공지/FAQ: Notice 엔티티/DTO, Repository, Service, Controller (FAQ 등록 포함) --- build.gradle | 38 ++-- gradlew | 0 .../com/JoinUs/dp/global/common/ApiPath.java | 24 --- .../JoinUs/dp/global/common/api/ApiPath.java | 15 ++ .../exception/GlobalExceptionHandler.java | 24 +++ .../common/exception/NotFoundException.java | 6 + .../dp/global/common/response/Response.java | 27 +++ .../global/common/security/JwtProvider.java | 18 ++ .../controller/ClubApplicationController.java | 118 ++++++++++++ .../application/dto/ClubApplicationDto.java | 65 +++++++ .../domain/application/dto/ClubSummary.java | 18 ++ .../application/entity/ClubApplication.java | 39 ++++ .../domain/application/entity/ClubStatus.java | 11 ++ .../application/entity/QuestionAnswer.java | 17 ++ .../repository/ClubApplicationRepository.java | 92 ++++++++++ .../service/ClubApplicationService.java | 170 ++++++++++++++++++ .../notice/controller/NoticeController.java | 46 +++++ .../notice/dto/NoticeCreateRequest.java | 20 +++ .../domain/notice/dto/NoticeResponse.java | 34 ++++ .../global/domain/notice/entity/Notice.java | 39 ++++ .../notice/repository/NoticeRepository.java | 75 ++++++++ .../domain/notice/service/NoticeService.java | 53 ++++++ 22 files changed, 911 insertions(+), 38 deletions(-) mode change 100644 => 100755 gradlew delete mode 100644 src/main/java/com/JoinUs/dp/global/common/ApiPath.java create mode 100644 src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java create mode 100644 src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java create mode 100644 src/main/java/com/JoinUs/dp/global/common/response/Response.java create mode 100644 src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java create mode 100644 src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java diff --git a/build.gradle b/build.gradle index f261f5d..4765ffa 100644 --- a/build.gradle +++ b/build.gradle @@ -25,23 +25,33 @@ configurations { repositories { mavenCentral() } - dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-websocket' - compileOnly 'org.projectlombok:lombok' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.h2database:h2' -// runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.security:spring-security-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + // Spring Boot Starters + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-websocket' + + // ✅ Lombok (버전 명시 + annotationProcessor) + compileOnly 'org.projectlombok:lombok:1.18.32' + annotationProcessor 'org.projectlombok:lombok:1.18.32' + + testCompileOnly 'org.projectlombok:lombok:1.18.32' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.32' + + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + runtimeOnly 'com.h2database:h2' + // runtimeOnly 'com.mysql:mysql-connector-j' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } + + tasks.named('test') { useJUnitPlatform() } diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/com/JoinUs/dp/global/common/ApiPath.java b/src/main/java/com/JoinUs/dp/global/common/ApiPath.java deleted file mode 100644 index 146dc83..0000000 --- a/src/main/java/com/JoinUs/dp/global/common/ApiPath.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.JoinUs.dp.global.common; - -import jakarta.annotation.PostConstruct; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -public class ApiPath { - public static final String API_ROOT = "/api/v1"; - - - // 도메인별 API_PATH 예시 - // public static final String USER_PATH = API_ROOT + "/user"; - - public static String H2_PATH; - - @Value("${spring.h2.console.path}") - private String h2Path; - - @PostConstruct - public void init() { - H2_PATH = h2Path; - } -} diff --git a/src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java b/src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java new file mode 100644 index 0000000..68b557a --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java @@ -0,0 +1,15 @@ +// src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java +package com.JoinUs.dp.global.common.api; + +public final class ApiPath { + private ApiPath() {} + + // 공지/FAQ + public static final String NOTICE_PATH = "/api/notices"; + public static final String CLUB_NOTICE_PATH = "/api/clubs/{clubId}/notice"; + + // ✅ 동아리 신청 + public static final String APPLICATIONS = "/api/applications"; + public static final String CLUB_APPLICATIONS = "/api/clubs/{clubId}/applications"; + public static final String DEPARTMENT_CLUBS = "/api/departments/{departmentId}/clubs"; +} diff --git a/src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java b/src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..00c3631 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,24 @@ +package com.JoinUs.dp.global.common.exception; + + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import com.JoinUs.dp.global.common.response.Response; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(NotFoundException.class) + public ResponseEntity> handleNotFound(NotFoundException ex) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(new Response<>(HttpStatus.NOT_FOUND.value(), null, ex.getMessage())); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleOther(Exception ex) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new Response<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), null, "서버 오류가 발생했습니다.")); + } +} \ No newline at end of file diff --git a/src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java b/src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java new file mode 100644 index 0000000..0f17979 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java @@ -0,0 +1,6 @@ +package com.JoinUs.dp.global.common.exception; + + +public class NotFoundException extends RuntimeException { + public NotFoundException(String msg) { super(msg); } +} diff --git a/src/main/java/com/JoinUs/dp/global/common/response/Response.java b/src/main/java/com/JoinUs/dp/global/common/response/Response.java new file mode 100644 index 0000000..8320254 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/common/response/Response.java @@ -0,0 +1,27 @@ +package com.JoinUs.dp.global.common.response; + + +import java.time.LocalDateTime; + +public class Response { + private int status; + private T data; + private String message; + private LocalDateTime timestamp = LocalDateTime.now(); + + public Response(int status, T data, String message) { + this.status = status; + this.data = data; + this.message = message; + } + + public int getStatus() { return status; } + public T getData() { return data; } + public String getMessage() { return message; } + public LocalDateTime getTimestamp() { return timestamp; } + + public void setStatus(int status) { this.status = status; } + public void setData(T data) { this.data = data; } + public void setMessage(String message) { this.message = message; } + public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } +} \ No newline at end of file diff --git a/src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java b/src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java new file mode 100644 index 0000000..4659c3c --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java @@ -0,0 +1,18 @@ +package com.JoinUs.dp.global.common.security; + + +import org.springframework.stereotype.Component; + +// 실제 JWT 파싱 대신 "Bearer 이메일" 형식에서 이메일만 잘라내는 스텁 예시 +@Component +public class JwtProvider { + public String extractEmail(String tokenHeader) { + if (tokenHeader == null) return null; + // 예: "Bearer user@example.com" + String prefix = "Bearer "; + if (tokenHeader.startsWith(prefix)) { + return tokenHeader.substring(prefix.length()).trim(); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java b/src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java new file mode 100644 index 0000000..d5d52ce --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java @@ -0,0 +1,118 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java +package com.JoinUs.dp.global.domain.application.controller; + +import java.util.List; +import java.util.Map; + +import org.springframework.http.*; +import org.springframework.web.bind.annotation.*; + +import com.JoinUs.dp.global.common.api.ApiPath; +import com.JoinUs.dp.global.common.response.Response; +import com.JoinUs.dp.global.domain.application.dto.ClubApplicationDto; +import com.JoinUs.dp.global.domain.application.dto.ClubSummary; +import com.JoinUs.dp.global.domain.application.service.ClubApplicationService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class ClubApplicationController { + + private final ClubApplicationService service; + + public ClubApplicationController(ClubApplicationService service) { + this.service = service; + } + + // 동아리 신청 등록: POST /api/applications + @PostMapping(ApiPath.APPLICATIONS) + public ResponseEntity> apply(@RequestBody ClubApplicationDto req) { + ClubApplicationDto saved = service.apply(req); + return ResponseEntity.status(HttpStatus.CREATED) + .body(new Response<>(HttpStatus.CREATED.value(), saved, "동아리 신청 등록 완료")); + } + + // 신청 목록 전체 조회: GET /api/applications + @GetMapping(ApiPath.APPLICATIONS) + public ResponseEntity>> findAll() { + List list = service.findAll(); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), list, "신청 목록 조회 성공")); + } + + // 신청 상세 조회: GET /api/applications/{applicationId} + @GetMapping(ApiPath.APPLICATIONS + "/{applicationId}") + public ResponseEntity> findById(@PathVariable("applicationId") Long applicationId) { + ClubApplicationDto dto = service.findById(applicationId); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), dto, "신청 상세 조회 성공")); + } + + // 신청 취소(삭제): DELETE /api/applications/{applicationId} + @DeleteMapping(ApiPath.APPLICATIONS + "/{applicationId}") + public ResponseEntity> cancel(@PathVariable("applicationId") Long applicationId) { + service.cancel(applicationId); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), null, "신청 취소(삭제) 완료")); + } + + // 신청 정보 수정(전체): PUT /api/applications + @PutMapping(ApiPath.APPLICATIONS) + public ResponseEntity> update(@RequestBody ClubApplicationDto req) { + ClubApplicationDto updated = service.update(req); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), updated, "신청 정보 수정 완료")); + } + + // 신청 정보 일부 수정: PATCH /api/applications/{applicationId} + @PatchMapping(ApiPath.APPLICATIONS + "/{applicationId}") + public ResponseEntity> partialUpdate( + @PathVariable("applicationId") Long applicationId, + @RequestBody Map updates) { + ClubApplicationDto updated = service.partialUpdate(applicationId, updates); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), updated, "신청 정보 일부 수정 완료")); + } + + // 특정 클럽의 신청자 목록 조회: GET /api/clubs/{clubId}/applications + @GetMapping(ApiPath.CLUB_APPLICATIONS) + public ResponseEntity>> findByClubId(@PathVariable("clubId") Long clubId) { + List list = service.findByClubId(clubId); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), list, "클럽별 신청자 목록 조회 성공")); + } + + // 합격 여부 설정(동아리장 전용): PATCH /api/applications/{applicationId}/result + @PatchMapping(ApiPath.APPLICATIONS + "/{applicationId}/result") + public ResponseEntity> setResult( + @PathVariable("applicationId") Long applicationId, + @RequestBody Map body) { + + String result = body.get("result"); + String message = body.get("message"); + ClubApplicationDto updated = service.setResult(applicationId, result, message); + + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), updated, "합격 여부 설정 완료")); + } + + // 합격자 최종 확정/철회(본인): PATCH /api/applications/{applicationId}/confirm + @PatchMapping(ApiPath.APPLICATIONS + "/{applicationId}/confirm") + public ResponseEntity> confirmOrDecline( + @PathVariable("applicationId") Long applicationId, + @RequestBody Map body) { + + String action = body.get("action"); + ClubApplicationDto updated = service.confirmOrDecline(applicationId, action); + + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), updated, "확정/철회 처리 완료")); + } + + // 불합격자 추가 합격 통보(동아리장 전용): PATCH /api/applications/{applicationId}/additional-offer + @PatchMapping(ApiPath.APPLICATIONS + "/{applicationId}/additional-offer") + public ResponseEntity> additionalOffer(@PathVariable("applicationId") Long applicationId) { + ClubApplicationDto updated = service.additionalOffer(applicationId); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), updated, "추가 합격 통보 완료")); + } + + // 학과별 동아리 목록 조회: GET /api/departments/{departmentId}/clubs + @GetMapping(ApiPath.DEPARTMENT_CLUBS) + public ResponseEntity>> getClubsByDepartment(@PathVariable("departmentId") Long departmentId) { + List list = service.getClubsByDepartment(departmentId); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), list, "학과별 클럽 목록 조회 성공")); + } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java b/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java new file mode 100644 index 0000000..b9812bc --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java @@ -0,0 +1,65 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java +package com.JoinUs.dp.global.domain.application.dto; + +import java.time.LocalDate; +import java.util.List; + +import com.JoinUs.dp.global.domain.application.entity.ClubApplication; +import com.JoinUs.dp.global.domain.application.entity.ClubStatus; +import com.JoinUs.dp.global.domain.application.entity.QuestionAnswer; + +public class ClubApplicationDto { + private Long id; + private Long clubId; + private Long departmentId; + private String applicantName; + private String clubName; + private LocalDate appliedDate; + private List questionAnswers; + private ClubStatus status; + private String resultMessage; + + public ClubApplicationDto() {} + + public ClubApplicationDto(Long id, Long clubId, Long departmentId, String applicantName, String clubName, + LocalDate appliedDate, List questionAnswers, + ClubStatus status, String resultMessage) { + this.id = id; + this.clubId = clubId; + this.departmentId = departmentId; + this.applicantName = applicantName; + this.clubName = clubName; + this.appliedDate = appliedDate; + this.questionAnswers = questionAnswers; + this.status = status; + this.resultMessage = resultMessage; + } + + public static ClubApplicationDto from(ClubApplication e) { + return new ClubApplicationDto( + e.getId(), e.getClubId(), e.getDepartmentId(), e.getApplicantName(), e.getClubName(), + e.getAppliedDate(), e.getQuestionAnswers(), e.getStatus(), e.getResultMessage() + ); + } + + // getters/setters + public Long getId() { return id; } + public Long getClubId() { return clubId; } + public Long getDepartmentId() { return departmentId; } + public String getApplicantName() { return applicantName; } + public String getClubName() { return clubName; } + public LocalDate getAppliedDate() { return appliedDate; } + public List getQuestionAnswers() { return questionAnswers; } + public ClubStatus getStatus() { return status; } + public String getResultMessage() { return resultMessage; } + + public void setId(Long id) { this.id = id; } + public void setClubId(Long clubId) { this.clubId = clubId; } + public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; } + public void setApplicantName(String applicantName) { this.applicantName = applicantName; } + public void setClubName(String clubName) { this.clubName = clubName; } + public void setAppliedDate(LocalDate appliedDate) { this.appliedDate = appliedDate; } + public void setQuestionAnswers(List questionAnswers) { this.questionAnswers = questionAnswers; } + public void setStatus(ClubStatus status) { this.status = status; } + public void setResultMessage(String resultMessage) { this.resultMessage = resultMessage; } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java b/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java new file mode 100644 index 0000000..e790f26 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java @@ -0,0 +1,18 @@ +// (선택) 부가 응답: 학과별 클럽 목록 요약 +// src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java +package com.JoinUs.dp.global.domain.application.dto; + +public class ClubSummary { + private Long clubId; + private String clubName; + + public ClubSummary() {} + public ClubSummary(Long clubId, String clubName) { + this.clubId = clubId; + this.clubName = clubName; + } + public Long getClubId() { return clubId; } + public String getClubName() { return clubName; } + public void setClubId(Long clubId) { this.clubId = clubId; } + public void setClubName(String clubName) { this.clubName = clubName; } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java b/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java new file mode 100644 index 0000000..22b530a --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java @@ -0,0 +1,39 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java +package com.JoinUs.dp.global.domain.application.entity; + +import java.time.LocalDate; +import java.util.List; + +public class ClubApplication { + private Long id; // 신청 ID + private Long clubId; // 클럽 ID + private Long departmentId; // 학과 ID + private String applicantName; // 신청자 이름 + private String clubName; // 클럽 이름 + private LocalDate appliedDate; // 신청 날짜 + private List questionAnswers; // 질문-답변 목록 + private ClubStatus status; // 신청 상태 + private String resultMessage; // 심사 결과 안내 문구 + + public ClubApplication() {} + + public Long getId() { return id; } + public Long getClubId() { return clubId; } + public Long getDepartmentId() { return departmentId; } + public String getApplicantName() { return applicantName; } + public String getClubName() { return clubName; } + public LocalDate getAppliedDate() { return appliedDate; } + public List getQuestionAnswers() { return questionAnswers; } + public ClubStatus getStatus() { return status; } + public String getResultMessage() { return resultMessage; } + + public void setId(Long id) { this.id = id; } + public void setClubId(Long clubId) { this.clubId = clubId; } + public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; } + public void setApplicantName(String applicantName) { this.applicantName = applicantName; } + public void setClubName(String clubName) { this.clubName = clubName; } + public void setAppliedDate(LocalDate appliedDate) { this.appliedDate = appliedDate; } + public void setQuestionAnswers(List questionAnswers) { this.questionAnswers = questionAnswers; } + public void setStatus(ClubStatus status) { this.status = status; } + public void setResultMessage(String resultMessage) { this.resultMessage = resultMessage; } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java b/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java new file mode 100644 index 0000000..a0c5fa6 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java @@ -0,0 +1,11 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java +package com.JoinUs.dp.global.domain.application.entity; + +public enum ClubStatus { + PENDING, // 심사중(신규) + PASSED, // 합격 (확정 대기) + FAILED, // 불합격 + CONFIRMED, // 합격자 최종 확정 + DECLINED // 합격자 철회 +} + diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java b/src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java new file mode 100644 index 0000000..de8ff86 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java @@ -0,0 +1,17 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java +package com.JoinUs.dp.global.domain.application.entity; + +public class QuestionAnswer { + private String question; + private String answer; + + public QuestionAnswer() {} + public QuestionAnswer(String question, String answer) { + this.question = question; + this.answer = answer; + } + public String getQuestion() { return question; } + public String getAnswer() { return answer; } + public void setQuestion(String question) { this.question = question; } + public void setAnswer(String answer) { this.answer = answer; } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java b/src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java new file mode 100644 index 0000000..77b88e1 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java @@ -0,0 +1,92 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java +package com.JoinUs.dp.global.domain.application.repository; + +import java.time.LocalDate; +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Repository; + +import com.JoinUs.dp.global.domain.application.dto.ClubSummary; +import com.JoinUs.dp.global.domain.application.entity.ClubApplication; +import com.JoinUs.dp.global.domain.application.entity.ClubStatus; +import com.JoinUs.dp.global.domain.application.entity.QuestionAnswer; + +@Repository +public class ClubApplicationRepository { + + private final Map storage = Collections.synchronizedMap(new LinkedHashMap<>()); + private final AtomicLong seq = new AtomicLong(1); + + public ClubApplicationRepository() { + // 샘플 데이터 + ClubApplication a = new ClubApplication(); + a.setId(seq.getAndIncrement()); + a.setClubId(101L); + a.setDepartmentId(11L); + a.setClubName("개발동아리"); + a.setApplicantName("홍길동"); + a.setAppliedDate(LocalDate.now().minusDays(2)); + a.setQuestionAnswers(List.of(new QuestionAnswer("지원 동기", "경험을 쌓고 싶어서"))); + a.setStatus(ClubStatus.PENDING); + storage.put(a.getId(), a); + + ClubApplication b = new ClubApplication(); + b.setId(seq.getAndIncrement()); + b.setClubId(202L); + b.setDepartmentId(22L); + b.setClubName("사진동아리"); + b.setApplicantName("이몽룡"); + b.setAppliedDate(LocalDate.now().minusDays(1)); + b.setQuestionAnswers(List.of(new QuestionAnswer("특기", "사진 보정"))); + b.setStatus(ClubStatus.PENDING); + storage.put(b.getId(), b); + } + + public ClubApplication save(ClubApplication app) { + if (app.getId() == null) { + app.setId(seq.getAndIncrement()); + } + if (app.getStatus() == null) { + app.setStatus(ClubStatus.PENDING); + } + if (app.getAppliedDate() == null) { + app.setAppliedDate(LocalDate.now()); + } + storage.put(app.getId(), app); + return app; + } + + public Optional findById(Long id) { + return Optional.ofNullable(storage.get(id)); + } + + public List findAll() { + return new ArrayList<>(storage.values()); + } + + public void deleteById(Long id) { + storage.remove(id); + } + + public List findByClubId(Long clubId) { + return storage.values().stream() + .filter(a -> Objects.equals(a.getClubId(), clubId)) + .collect(Collectors.toList()); + } + + public List findClubsByDepartmentId(Long departmentId) { + // 신청 데이터 기반으로 학과 내 "존재하는" 클럽 목록 요약 + return storage.values().stream() + .filter(a -> Objects.equals(a.getDepartmentId(), departmentId)) + .collect(Collectors.toMap( + ClubApplication::getClubId, + ClubApplication::getClubName, + (v1, v2) -> v1 + )) + .entrySet().stream() + .map(e -> new ClubSummary(e.getKey(), e.getValue())) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java b/src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java new file mode 100644 index 0000000..9b7a9be --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java @@ -0,0 +1,170 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java +package com.JoinUs.dp.global.domain.application.service; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +// import org.springframework.transaction.annotation.Transactional; // ← 트랜잭션이 필요하면 주석 해제 + +import com.JoinUs.dp.global.common.exception.NotFoundException; +import com.JoinUs.dp.global.domain.application.dto.ClubApplicationDto; +import com.JoinUs.dp.global.domain.application.dto.ClubSummary; +import com.JoinUs.dp.global.domain.application.entity.ClubApplication; +import com.JoinUs.dp.global.domain.application.entity.ClubStatus; +import com.JoinUs.dp.global.domain.application.repository.ClubApplicationRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ClubApplicationService { + + // ✅ 잘못된 자기 주입 제거, Repository 주입으로 수정 + private final ClubApplicationRepository repository; + public ClubApplicationService(ClubApplicationRepository repository) { + this.repository = repository; + } + + // -------------------- 기본 CRUD -------------------- + + // @Transactional + public ClubApplicationDto apply(ClubApplicationDto dto) { + ClubApplication e = toEntity(dto); + e.setId(null); + e.setStatus(ClubStatus.PENDING); + e.setAppliedDate(e.getAppliedDate() == null ? LocalDate.now() : e.getAppliedDate()); + return toDto(repository.save(e)); + } + + public List findAll() { + return repository.findAll().stream().map(this::toDto).toList(); + } + + public ClubApplicationDto findById(Long id) { + ClubApplication e = repository.findById(id) + .orElseThrow(() -> new NotFoundException("신청을 찾을 수 없습니다. id=" + id)); + return toDto(e); + } + + // @Transactional + public void cancel(Long id) { + if (repository.findById(id).isEmpty()) { + throw new NotFoundException("신청을 찾을 수 없습니다. id=" + id); + } + repository.deleteById(id); + } + + // @Transactional + public ClubApplicationDto update(ClubApplicationDto dto) { + if (dto.getId() == null) throw new IllegalArgumentException("id는 필수입니다."); + ClubApplication e = repository.findById(dto.getId()) + .orElseThrow(() -> new NotFoundException("신청을 찾을 수 없습니다. id=" + dto.getId())); + + e.setClubId(dto.getClubId()); + e.setDepartmentId(dto.getDepartmentId()); + e.setApplicantName(dto.getApplicantName()); + e.setClubName(dto.getClubName()); + e.setAppliedDate(dto.getAppliedDate()); + e.setQuestionAnswers(dto.getQuestionAnswers()); + e.setStatus(dto.getStatus()); + e.setResultMessage(dto.getResultMessage()); + + return toDto(repository.save(e)); + } + + // @Transactional + public ClubApplicationDto partialUpdate(Long id, Map updates) { + ClubApplication e = repository.findById(id) + .orElseThrow(() -> new NotFoundException("신청을 찾을 수 없습니다. id=" + id)); + + if (updates.containsKey("clubId")) e.setClubId(Long.valueOf(updates.get("clubId").toString())); + if (updates.containsKey("departmentId")) e.setDepartmentId(Long.valueOf(updates.get("departmentId").toString())); + if (updates.containsKey("applicantName")) e.setApplicantName((String) updates.get("applicantName")); + if (updates.containsKey("clubName")) e.setClubName((String) updates.get("clubName")); + if (updates.containsKey("appliedDate")) e.setAppliedDate(LocalDate.parse((String) updates.get("appliedDate"))); + if (updates.containsKey("resultMessage")) e.setResultMessage((String) updates.get("resultMessage")); + // NOTE: questionAnswers / status는 별도 DTO/검증을 권장 + + return toDto(repository.save(e)); + } + + // -------------------- 추가 기능 -------------------- + + public List findByClubId(Long clubId) { + return repository.findByClubId(clubId).stream().map(this::toDto).toList(); + } + + // @Transactional + public ClubApplicationDto setResult(Long id, String result, String message) { + ClubApplication e = repository.findById(id) + .orElseThrow(() -> new NotFoundException("신청을 찾을 수 없습니다. id=" + id)); + + if ("pass".equalsIgnoreCase(result)) { + e.setStatus(ClubStatus.PASSED); + } else if ("fail".equalsIgnoreCase(result)) { + e.setStatus(ClubStatus.FAILED); + } else { + throw new IllegalArgumentException("result 값은 pass 또는 fail 이어야 합니다."); + } + e.setResultMessage(message); + return toDto(repository.save(e)); + } + + // @Transactional + public ClubApplicationDto confirmOrDecline(Long id, String action) { + ClubApplication e = repository.findById(id) + .orElseThrow(() -> new NotFoundException("신청을 찾을 수 없습니다. id=" + id)); + + // 기본 정책: PASSED 상태에서 확정/철회 가능, (옵션) 확정 후 철회도 허용 + if (!(e.getStatus() == ClubStatus.PASSED || e.getStatus() == ClubStatus.CONFIRMED)) { + throw new IllegalStateException("현재 상태에서 확정/철회할 수 없습니다. status=" + e.getStatus()); + } + + if ("confirm".equalsIgnoreCase(action)) { + e.setStatus(ClubStatus.CONFIRMED); + } else if ("decline".equalsIgnoreCase(action)) { + e.setStatus(ClubStatus.DECLINED); + } else { + throw new IllegalArgumentException("action 값은 confirm 또는 decline 이어야 합니다."); + } + return toDto(repository.save(e)); + } + + // @Transactional + public ClubApplicationDto additionalOffer(Long id) { + ClubApplication e = repository.findById(id) + .orElseThrow(() -> new NotFoundException("신청을 찾을 수 없습니다. id=" + id)); + if (e.getStatus() != ClubStatus.FAILED) { + throw new IllegalStateException("FAILED 상태인 지원자에게만 추가 합격 통보가 가능합니다."); + } + e.setStatus(ClubStatus.PASSED); + e.setResultMessage("추가 합격 통보"); + return toDto(repository.save(e)); + } + + public List getClubsByDepartment(Long departmentId) { + return repository.findClubsByDepartmentId(departmentId); + } + + // -------------------- 내부 변환 -------------------- + + private ClubApplicationDto toDto(ClubApplication e) { + return ClubApplicationDto.from(e); + } + + private ClubApplication toEntity(ClubApplicationDto dto) { + ClubApplication e = new ClubApplication(); + e.setId(dto.getId()); + e.setClubId(dto.getClubId()); + e.setDepartmentId(dto.getDepartmentId()); + e.setApplicantName(dto.getApplicantName()); + e.setClubName(dto.getClubName()); + e.setAppliedDate(dto.getAppliedDate()); + e.setQuestionAnswers(dto.getQuestionAnswers()); + e.setStatus(dto.getStatus()); + e.setResultMessage(dto.getResultMessage()); + return e; + } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java b/src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java new file mode 100644 index 0000000..1686859 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java @@ -0,0 +1,46 @@ +// src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java +package com.JoinUs.dp.global.domain.notice.controller; + +import com.JoinUs.dp.global.common.response.Response; +import com.JoinUs.dp.global.domain.notice.dto.NoticeCreateRequest; +import com.JoinUs.dp.global.domain.notice.dto.NoticeResponse; +import com.JoinUs.dp.global.domain.notice.service.NoticeService; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import com.JoinUs.dp.global.common.api.ApiPath; + +@RestController +@RequestMapping(ApiPath.NOTICE_PATH) +@RequiredArgsConstructor +public class NoticeController { + + private final NoticeService noticeService; // ✅ new 금지, DI 받기 + // ✅ 명시적 생성자 주입 + public NoticeController(NoticeService noticeService) { + this.noticeService = noticeService; + } + + @GetMapping + public ResponseEntity>> getFaqs() { + List list = noticeService.getFaqList(); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), list, "FAQ 목록 조회 성공")); + } + + @GetMapping("/{noticeId}") + public ResponseEntity> getDetail(@PathVariable("noticeId") Long noticeId) { + NoticeResponse dto = noticeService.getNoticeDetail(noticeId); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), dto, "공지 상세 조회 성공")); + } + // ✅ FAQ 등록 (중복 경로 제거) + @PostMapping + public ResponseEntity> createFaq(@RequestBody NoticeCreateRequest req) { + NoticeResponse saved = noticeService.createFaq(req.getTitle(), req.getContent()); + return ResponseEntity.ok(new Response<>(HttpStatus.OK.value(), saved, "FAQ 등록 성공")); + } + +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java b/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java new file mode 100644 index 0000000..8e8dbb9 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java @@ -0,0 +1,20 @@ +package com.JoinUs.dp.global.domain.notice.dto; + + +public class NoticeCreateRequest { + private String title; + private String content; + + public NoticeCreateRequest() {} + + public NoticeCreateRequest(String title, String content) { + this.title = title; + this.content = content; + } + + public String getTitle() { return title; } + public String getContent() { return content; } + + public void setTitle(String title) { this.title = title; } + public void setContent(String content) { this.content = content; } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java b/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java new file mode 100644 index 0000000..2b421a3 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java @@ -0,0 +1,34 @@ +package com.JoinUs.dp.global.domain.notice.dto; + +import java.time.LocalDateTime; + +import com.JoinUs.dp.global.domain.notice.entity.Notice; + +public class NoticeResponse { + private Long id; + private Long clubId; + private String title; + private String content; + private LocalDateTime createdAt; + private Notice.Type type; + + public NoticeResponse(Long id, Long clubId, String title, String content, LocalDateTime createdAt, Notice.Type type) { + this.id = id; + this.clubId = clubId; + this.title = title; + this.content = content; + this.createdAt = createdAt; + this.type = type; + } + + public Long getId() { return id; } + public Long getClubId() { return clubId; } + public String getTitle() { return title; } + public String getContent() { return content; } + public LocalDateTime getCreatedAt() { return createdAt; } + public Notice.Type getType() { return type; } + + public static NoticeResponse from(Notice n) { + return new NoticeResponse(n.getId(), n.getClubId(), n.getTitle(), n.getContent(), n.getCreatedAt(), n.getType()); + } +} \ No newline at end of file diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java b/src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java new file mode 100644 index 0000000..b20fdac --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java @@ -0,0 +1,39 @@ +package com.JoinUs.dp.global.domain.notice.entity; + +import java.time.LocalDateTime; + +public class Notice { + public enum Type { + FAQ, CLUB_NOTICE + } + + private Long id; + private Long clubId; // 클럽 공지일 때만 세팅 (FAQ는 null) + private String title; + private String content; + private LocalDateTime createdAt; + private Type type; + + public Notice(Long id, Long clubId, String title, String content, LocalDateTime createdAt, Type type) { + this.id = id; + this.clubId = clubId; + this.title = title; + this.content = content; + this.createdAt = createdAt; + this.type = type; + } + + public Long getId() { return id; } + public Long getClubId() { return clubId; } + public String getTitle() { return title; } + public String getContent() { return content; } + public LocalDateTime getCreatedAt() { return createdAt; } + public Type getType() { return type; } + + public void setId(Long id) { this.id = id; } + public void setClubId(Long clubId) { this.clubId = clubId; } + public void setTitle(String title) { this.title = title; } + public void setContent(String content) { this.content = content; } + public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } + public void setType(Type type) { this.type = type; } +} \ No newline at end of file diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java b/src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java new file mode 100644 index 0000000..a583ef2 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java @@ -0,0 +1,75 @@ +package com.JoinUs.dp.global.domain.notice.repository; + +import org.springframework.stereotype.Repository; + +import com.JoinUs.dp.global.domain.notice.entity.Notice; +import com.JoinUs.dp.global.domain.notice.entity.Notice.Type; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +@Repository +public class NoticeRepository { + + private final List store = Collections.synchronizedList(new ArrayList<>()); + private final AtomicLong seq = new AtomicLong(1); + + public NoticeRepository() { + // 샘플 데이터 (FAQ 2개, 클럽공지 2개) + store.add(new Notice(seq.getAndIncrement(), null, "가입/로그인 안내", "회원가입 및 로그인 방법을 안내합니다.", LocalDateTime.now().minusDays(5), Type.FAQ)); + store.add(new Notice(seq.getAndIncrement(), null, "문의 처리시간", "문의는 평일 10:00~18:00 처리됩니다.", LocalDateTime.now().minusDays(3), Type.FAQ)); + store.add(new Notice(seq.getAndIncrement(), 101L, "개발동아리 세미나 공지", "8/25(월) 18:00, 3층 세미나실", LocalDateTime.now().minusDays(2), Type.CLUB_NOTICE)); + store.add(new Notice(seq.getAndIncrement(), 202L, "사진동아리 출사 공지", "8/28(목) 10:00, 정문 집결", LocalDateTime.now().minusDays(1), Type.CLUB_NOTICE)); + } + + /** FAQ 전체 조회 */ + public List findAllFaq() { + return store.stream() + .filter(n -> n.getType() == Type.FAQ) + .sorted(Comparator.comparing(Notice::getCreatedAt).reversed()) + .collect(Collectors.toList()); + } + + /** 단건 조회 */ + public Optional findById(Long id) { + return store.stream().filter(n -> Objects.equals(n.getId(), id)).findFirst(); + } + + /** 특정 클럽 공지 목록 */ + public List findByClubId(Long clubId) { + return store.stream() + .filter(n -> n.getType() == Type.CLUB_NOTICE && Objects.equals(n.getClubId(), clubId)) + .sorted(Comparator.comparing(Notice::getCreatedAt).reversed()) + .collect(Collectors.toList()); + } + + /** 클럽 공지 등록 */ + public Notice saveClubNotice(Long clubId, String title, String content) { + Notice n = new Notice( + seq.getAndIncrement(), + clubId, + title, + content, + LocalDateTime.now(), + Type.CLUB_NOTICE + ); + store.add(n); + return n; + } + + /** ✅ FAQ 등록 (요청하신 메서드 추가) */ + public Notice saveFaq(String title, String content) { + Notice n = new Notice( + seq.getAndIncrement(), + null, // clubId 없음 + title, + content, + LocalDateTime.now(), + Type.FAQ + ); + store.add(n); + return n; + } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java b/src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java new file mode 100644 index 0000000..1732d33 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java @@ -0,0 +1,53 @@ +package com.JoinUs.dp.global.domain.notice.service; + +import com.JoinUs.dp.global.common.exception.NotFoundException; +import com.JoinUs.dp.global.domain.notice.dto.NoticeResponse; +import com.JoinUs.dp.global.domain.notice.entity.Notice; +import com.JoinUs.dp.global.domain.notice.repository.NoticeRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NoticeService { + + private final NoticeRepository noticeRepository; + // ✅ 명시적 생성자 주입 + public NoticeService(NoticeRepository noticeRepository) { + this.noticeRepository = noticeRepository; + } + // FAQ 목록 + public List getFaqList() { + return noticeRepository.findAllFaq().stream() + .map(NoticeResponse::from) + .toList(); + } + + // 공지 상세 (FAQ/클럽공지 공통) + public NoticeResponse getNoticeDetail(Long id) { + Notice n = noticeRepository.findById(id) + .orElseThrow(() -> new NotFoundException("해당 공지를 찾을 수 없습니다. id=" + id)); + return NoticeResponse.from(n); + } + + // 특정 클럽 공지 목록 + public List getClubNotices(Long clubId) { + return noticeRepository.findByClubId(clubId).stream() + .map(NoticeResponse::from) + .toList(); + } + + // 특정 클럽 공지 등록 + public NoticeResponse createClubNotice(Long clubId, String title, String content) { + Notice saved = noticeRepository.saveClubNotice(clubId, title, content); + return NoticeResponse.from(saved); + } + + // ✅ FAQ 등록 (Repository.saveFaq 사용) + public NoticeResponse createFaq(String title, String content) { + Notice saved = noticeRepository.saveFaq(title, content); + return NoticeResponse.from(saved); + } +} From fcf268794415bb9a7eb267f27f1ad7ee525253d5 Mon Sep 17 00:00:00 2001 From: kjin <125746524+kjinhub@users.noreply.github.com> Date: Sat, 23 Aug 2025 19:45:21 +0900 Subject: [PATCH 4/8] Update README.md --- README.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 04f20d2..b2f9b08 100644 --- a/README.md +++ b/README.md @@ -1 +1,65 @@ -# ClubApplicationPlatform_Server \ No newline at end of file +[파일 역할 정리] +📌 공통 모듈 + +ApiPath.java → 모든 API 엔드포인트 경로를 상수로 정의 (공지/FAQ, 동아리 신청 등). +Response.java → 공통 응답 객체(상태 코드, 데이터, 메시지, 타임스탬프 포함). +NotFoundException.java → 데이터 미존재 시 예외 정의. +GlobalExceptionHandler.java → 예외를 공통적으로 처리(404/500 반환). +JwtProvider.java → JWT 스텁, "Bearer 이메일" 형식에서 이메일 추출. +SecurityConfiguration.java → Spring Security 설정 (CSRF, FormLogin 비활성화, 세션 정책). + +📌 동아리 신청 (ClubApplication) 도메인 + +ClubApplication.java → 신청 엔티티 (신청자, 클럽, 학과, 질문답변, 상태 등). +ClubStatus.java → 신청 상태(PENDING, PASSED, FAILED, CONFIRMED, DECLINED). +QuestionAnswer.java → 지원자가 입력한 질문/답변 구조. +ClubApplicationDto.java → DTO (엔티티 ↔ 응답 변환). +ClubSummary.java → 학과별 클럽 요약 DTO (id, 이름). +ClubApplicationRepository.java → 인메모리 저장소(Map), CRUD, 클럽/학과별 조회 지원. +ClubApplicationService.java → 비즈니스 로직 (신청 등록/조회/수정/취소, 합격 여부, 확정/철회, 추가 합격). +ClubApplicationController.java → REST API 제공 (POST/GET/PUT/PATCH/DELETE). + +📌 공지 / FAQ (Notice) 도메인 + +Notice.java → 공지 엔티티 (id, clubId, 제목, 내용, 작성일, 타입(FAQ/CLUB_NOTICE)). + +NoticeResponse.java → 응답 DTO (엔티티 → 클라이언트 응답 변환). +NoticeCreateRequest.java → 공지/FAQ 등록 요청 DTO. +NoticeRepository.java → 인메모리 저장소(List), 샘플 데이터 4건 포함, FAQ/공지 CRUD. +NoticeService.java → 공지/FAQ 비즈니스 로직 (FAQ 목록, 상세조회, 등록, 클럽 공지). +NoticeController.java → REST API 제공 (FAQ 목록 조회, 공지 상세 조회, FAQ 등록). + +📌 부트스트랩 +DpApplication.java → Spring Boot 메인 실행 클래스. + + +[END POINT 기능 정리] + +POST /api/applications +→ 신규 동아리 신청 등록 + +GET /api/applications +→ 전체 신청 목록 조회 + +GET /api/applications/{applicationId} +→ 특정 신청 상세 조회 + +DELETE /api/applications/{applicationId} +→ 신청 취소(삭제) + +PUT /api/applications +→ 신청 정보 전체 수정 + +PATCH /api/applications/{applicationId} +→ 신청 정보 일부 수정 + +FAQ 관련 + +GET /api/notices +→ FAQ 목록 조회 + +GET /api/notices/{noticeId} +→ 공지 상세 조회 (FAQ/클럽공지 공통) + +POST /api/notices +→ FAQ 등록 From 4e8162781b9896e644e345a6e8e7847d225bcafb Mon Sep 17 00:00:00 2001 From: kjin <125746524+kjinhub@users.noreply.github.com> Date: Sat, 23 Aug 2025 19:47:26 +0900 Subject: [PATCH 5/8] Update README.md --- README.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2f9b08..89c1a2b 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ NoticeController.java → REST API 제공 (FAQ 목록 조회, 공지 상세 조 📌 부트스트랩 DpApplication.java → Spring Boot 메인 실행 클래스. - -[END POINT 기능 정리] +📌 END POINT 기능 정리 +🔹 동아리 신청 (ClubApplicationController) +기본 CRUD POST /api/applications → 신규 동아리 신청 등록 @@ -53,7 +54,24 @@ PUT /api/applications PATCH /api/applications/{applicationId} → 신청 정보 일부 수정 -FAQ 관련 +추가 기능 + +GET /api/clubs/{clubId}/applications +→ 특정 클럽의 신청자 목록 조회 + +PATCH /api/applications/{applicationId}/result +→ 합격 여부 설정 (result=pass|fail, message 포함, JSON Body 방식) + +PATCH /api/applications/{applicationId}/confirm +→ 합격자 본인의 확정(confirm) / 철회(decline) + +PATCH /api/applications/{applicationId}/additional-offer +→ 불합격자 추가 합격 통보 (FAILED → PASSED) + +GET /api/departments/{departmentId}/clubs +→ 학과별 클럽 목록 조회 + +🔹 공지 / FAQ (NoticeController) GET /api/notices → FAQ 목록 조회 From 06843c92a67026dca0da7e949663073a883e8747 Mon Sep 17 00:00:00 2001 From: kjinhub Date: Tue, 23 Sep 2025 12:06:42 +0900 Subject: [PATCH 6/8] =?UTF-8?q?pull=20requset=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EA=B2=BD=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dp/{global => }/common/api/ApiPath.java | 2 +- .../exception/GlobalExceptionHandler.java | 4 +-- .../common/exception/NotFoundException.java | 2 +- .../common/response/Response.java | 2 +- .../common/security/JwtProvider.java | 2 +- .../config/SecurityConfiguration.java | 2 +- .../controller/ClubApplicationController.java | 12 +++---- .../controller/NoticeController.java | 13 ++++---- .../com/JoinUs/dp/controller/Snippet.java | 7 +++++ .../dto/ClubApplicationDto.java | 8 ++--- .../application => }/dto/ClubSummary.java | 2 +- .../notice => }/dto/NoticeCreateRequest.java | 2 +- .../notice => }/dto/NoticeResponse.java | 4 +-- .../com/JoinUs/dp/dto/QuestionAnswerDto.java | 31 +++++++++++++++++++ .../entity/ClubApplication.java | 2 +- .../application => }/entity/ClubStatus.java | 2 +- .../domain/notice => }/entity/Notice.java | 2 +- .../entity/QuestionAnswer.java | 2 +- .../repository/ClubApplicationRepository.java | 10 +++--- .../repository/NoticeRepository.java | 6 ++-- .../service/ClubApplicationService.java | 14 ++++----- .../notice => }/service/NoticeService.java | 11 ++++--- .../com/JoinUs/dp/DpApplicationTests.java | 13 -------- 23 files changed, 91 insertions(+), 64 deletions(-) rename src/main/java/com/JoinUs/dp/{global => }/common/api/ApiPath.java (93%) rename src/main/java/com/JoinUs/dp/{global => }/common/exception/GlobalExceptionHandler.java (88%) rename src/main/java/com/JoinUs/dp/{global => }/common/exception/NotFoundException.java (71%) rename src/main/java/com/JoinUs/dp/{global => }/common/response/Response.java (94%) rename src/main/java/com/JoinUs/dp/{global => }/common/security/JwtProvider.java (91%) rename src/main/java/com/JoinUs/dp/{global => }/config/SecurityConfiguration.java (98%) rename src/main/java/com/JoinUs/dp/{global/domain/application => }/controller/ClubApplicationController.java (93%) rename src/main/java/com/JoinUs/dp/{global/domain/notice => }/controller/NoticeController.java (81%) create mode 100644 src/main/java/com/JoinUs/dp/controller/Snippet.java rename src/main/java/com/JoinUs/dp/{global/domain/application => }/dto/ClubApplicationDto.java (91%) rename src/main/java/com/JoinUs/dp/{global/domain/application => }/dto/ClubSummary.java (92%) rename src/main/java/com/JoinUs/dp/{global/domain/notice => }/dto/NoticeCreateRequest.java (91%) rename src/main/java/com/JoinUs/dp/{global/domain/notice => }/dto/NoticeResponse.java (90%) create mode 100644 src/main/java/com/JoinUs/dp/dto/QuestionAnswerDto.java rename src/main/java/com/JoinUs/dp/{global/domain/application => }/entity/ClubApplication.java (97%) rename src/main/java/com/JoinUs/dp/{global/domain/application => }/entity/ClubStatus.java (84%) rename src/main/java/com/JoinUs/dp/{global/domain/notice => }/entity/Notice.java (96%) rename src/main/java/com/JoinUs/dp/{global/domain/application => }/entity/QuestionAnswer.java (91%) rename src/main/java/com/JoinUs/dp/{global/domain/application => }/repository/ClubApplicationRepository.java (90%) rename src/main/java/com/JoinUs/dp/{global/domain/notice => }/repository/NoticeRepository.java (94%) rename src/main/java/com/JoinUs/dp/{global/domain/application => }/service/ClubApplicationService.java (93%) rename src/main/java/com/JoinUs/dp/{global/domain/notice => }/service/NoticeService.java (84%) delete mode 100644 src/test/java/com/JoinUs/dp/DpApplicationTests.java diff --git a/src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java b/src/main/java/com/JoinUs/dp/common/api/ApiPath.java similarity index 93% rename from src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java rename to src/main/java/com/JoinUs/dp/common/api/ApiPath.java index 68b557a..b3235b1 100644 --- a/src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java +++ b/src/main/java/com/JoinUs/dp/common/api/ApiPath.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/common/api/ApiPath.java -package com.JoinUs.dp.global.common.api; +package com.JoinUs.dp.common.api; public final class ApiPath { private ApiPath() {} diff --git a/src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java b/src/main/java/com/JoinUs/dp/common/exception/GlobalExceptionHandler.java similarity index 88% rename from src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java rename to src/main/java/com/JoinUs/dp/common/exception/GlobalExceptionHandler.java index 00c3631..46fbe82 100644 --- a/src/main/java/com/JoinUs/dp/global/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/JoinUs/dp/common/exception/GlobalExceptionHandler.java @@ -1,11 +1,11 @@ -package com.JoinUs.dp.global.common.exception; +package com.JoinUs.dp.common.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import com.JoinUs.dp.global.common.response.Response; +import com.JoinUs.dp.common.response.Response; @ControllerAdvice public class GlobalExceptionHandler { diff --git a/src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java b/src/main/java/com/JoinUs/dp/common/exception/NotFoundException.java similarity index 71% rename from src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java rename to src/main/java/com/JoinUs/dp/common/exception/NotFoundException.java index 0f17979..f4ef337 100644 --- a/src/main/java/com/JoinUs/dp/global/common/exception/NotFoundException.java +++ b/src/main/java/com/JoinUs/dp/common/exception/NotFoundException.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.global.common.exception; +package com.JoinUs.dp.common.exception; public class NotFoundException extends RuntimeException { diff --git a/src/main/java/com/JoinUs/dp/global/common/response/Response.java b/src/main/java/com/JoinUs/dp/common/response/Response.java similarity index 94% rename from src/main/java/com/JoinUs/dp/global/common/response/Response.java rename to src/main/java/com/JoinUs/dp/common/response/Response.java index 8320254..aff3354 100644 --- a/src/main/java/com/JoinUs/dp/global/common/response/Response.java +++ b/src/main/java/com/JoinUs/dp/common/response/Response.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.global.common.response; +package com.JoinUs.dp.common.response; import java.time.LocalDateTime; diff --git a/src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java b/src/main/java/com/JoinUs/dp/common/security/JwtProvider.java similarity index 91% rename from src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java rename to src/main/java/com/JoinUs/dp/common/security/JwtProvider.java index 4659c3c..03972fe 100644 --- a/src/main/java/com/JoinUs/dp/global/common/security/JwtProvider.java +++ b/src/main/java/com/JoinUs/dp/common/security/JwtProvider.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.global.common.security; +package com.JoinUs.dp.common.security; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/JoinUs/dp/global/config/SecurityConfiguration.java b/src/main/java/com/JoinUs/dp/config/SecurityConfiguration.java similarity index 98% rename from src/main/java/com/JoinUs/dp/global/config/SecurityConfiguration.java rename to src/main/java/com/JoinUs/dp/config/SecurityConfiguration.java index dc231ed..bf28a1c 100644 --- a/src/main/java/com/JoinUs/dp/global/config/SecurityConfiguration.java +++ b/src/main/java/com/JoinUs/dp/config/SecurityConfiguration.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.global.config; +package com.JoinUs.dp.config; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java b/src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java similarity index 93% rename from src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java rename to src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java index d5d52ce..1cf30a9 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java +++ b/src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/domain/application/controller/ClubApplicationController.java -package com.JoinUs.dp.global.domain.application.controller; +package com.JoinUs.dp.controller; import java.util.List; import java.util.Map; @@ -7,11 +7,11 @@ import org.springframework.http.*; import org.springframework.web.bind.annotation.*; -import com.JoinUs.dp.global.common.api.ApiPath; -import com.JoinUs.dp.global.common.response.Response; -import com.JoinUs.dp.global.domain.application.dto.ClubApplicationDto; -import com.JoinUs.dp.global.domain.application.dto.ClubSummary; -import com.JoinUs.dp.global.domain.application.service.ClubApplicationService; +import com.JoinUs.dp.common.api.ApiPath; +import com.JoinUs.dp.common.response.Response; +import com.JoinUs.dp.dto.ClubApplicationDto; +import com.JoinUs.dp.dto.ClubSummary; +import com.JoinUs.dp.service.ClubApplicationService; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java b/src/main/java/com/JoinUs/dp/controller/NoticeController.java similarity index 81% rename from src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java rename to src/main/java/com/JoinUs/dp/controller/NoticeController.java index 1686859..6e7a7aa 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java +++ b/src/main/java/com/JoinUs/dp/controller/NoticeController.java @@ -1,10 +1,12 @@ // src/main/java/com/JoinUs/dp/global/domain/notice/controller/NoticeController.java -package com.JoinUs.dp.global.domain.notice.controller; +package com.JoinUs.dp.controller; + +import com.JoinUs.dp.common.api.ApiPath; +import com.JoinUs.dp.common.response.Response; +import com.JoinUs.dp.dto.NoticeCreateRequest; +import com.JoinUs.dp.dto.NoticeResponse; +import com.JoinUs.dp.service.NoticeService; -import com.JoinUs.dp.global.common.response.Response; -import com.JoinUs.dp.global.domain.notice.dto.NoticeCreateRequest; -import com.JoinUs.dp.global.domain.notice.dto.NoticeResponse; -import com.JoinUs.dp.global.domain.notice.service.NoticeService; import lombok.RequiredArgsConstructor; import java.util.List; @@ -12,7 +14,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import com.JoinUs.dp.global.common.api.ApiPath; @RestController @RequestMapping(ApiPath.NOTICE_PATH) diff --git a/src/main/java/com/JoinUs/dp/controller/Snippet.java b/src/main/java/com/JoinUs/dp/controller/Snippet.java new file mode 100644 index 0000000..b9336fc --- /dev/null +++ b/src/main/java/com/JoinUs/dp/controller/Snippet.java @@ -0,0 +1,7 @@ +package com.JoinUs.dp.controller; + +public class Snippet { + public static void main(String[] args) { + } +} + diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java b/src/main/java/com/JoinUs/dp/dto/ClubApplicationDto.java similarity index 91% rename from src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java rename to src/main/java/com/JoinUs/dp/dto/ClubApplicationDto.java index b9812bc..e46e8de 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java +++ b/src/main/java/com/JoinUs/dp/dto/ClubApplicationDto.java @@ -1,12 +1,12 @@ // src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubApplicationDto.java -package com.JoinUs.dp.global.domain.application.dto; +package com.JoinUs.dp.dto; import java.time.LocalDate; import java.util.List; -import com.JoinUs.dp.global.domain.application.entity.ClubApplication; -import com.JoinUs.dp.global.domain.application.entity.ClubStatus; -import com.JoinUs.dp.global.domain.application.entity.QuestionAnswer; +import com.JoinUs.dp.entity.ClubApplication; +import com.JoinUs.dp.entity.ClubStatus; +import com.JoinUs.dp.entity.QuestionAnswer; public class ClubApplicationDto { private Long id; diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java b/src/main/java/com/JoinUs/dp/dto/ClubSummary.java similarity index 92% rename from src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java rename to src/main/java/com/JoinUs/dp/dto/ClubSummary.java index e790f26..5118d61 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java +++ b/src/main/java/com/JoinUs/dp/dto/ClubSummary.java @@ -1,6 +1,6 @@ // (선택) 부가 응답: 학과별 클럽 목록 요약 // src/main/java/com/JoinUs/dp/global/domain/application/dto/ClubSummary.java -package com.JoinUs.dp.global.domain.application.dto; +package com.JoinUs.dp.dto; public class ClubSummary { private Long clubId; diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java b/src/main/java/com/JoinUs/dp/dto/NoticeCreateRequest.java similarity index 91% rename from src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java rename to src/main/java/com/JoinUs/dp/dto/NoticeCreateRequest.java index 8e8dbb9..1f4469d 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeCreateRequest.java +++ b/src/main/java/com/JoinUs/dp/dto/NoticeCreateRequest.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.global.domain.notice.dto; +package com.JoinUs.dp.dto; public class NoticeCreateRequest { diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java b/src/main/java/com/JoinUs/dp/dto/NoticeResponse.java similarity index 90% rename from src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java rename to src/main/java/com/JoinUs/dp/dto/NoticeResponse.java index 2b421a3..9266e31 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/notice/dto/NoticeResponse.java +++ b/src/main/java/com/JoinUs/dp/dto/NoticeResponse.java @@ -1,8 +1,8 @@ -package com.JoinUs.dp.global.domain.notice.dto; +package com.JoinUs.dp.dto; import java.time.LocalDateTime; -import com.JoinUs.dp.global.domain.notice.entity.Notice; +import com.JoinUs.dp.entity.Notice; public class NoticeResponse { private Long id; diff --git a/src/main/java/com/JoinUs/dp/dto/QuestionAnswerDto.java b/src/main/java/com/JoinUs/dp/dto/QuestionAnswerDto.java new file mode 100644 index 0000000..9b40ed8 --- /dev/null +++ b/src/main/java/com/JoinUs/dp/dto/QuestionAnswerDto.java @@ -0,0 +1,31 @@ +// src/main/java/com/JoinUs/dp/global/domain/application/dto/QuestionAnswerDto.java +package com.JoinUs.dp.dto; + +import com.JoinUs.dp.entity.QuestionAnswer; + +public class QuestionAnswerDto { + private String question; + private String answer; + + public QuestionAnswerDto() {} + + public QuestionAnswerDto(String question, String answer) { + this.question = question; + this.answer = answer; + } + + public String getQuestion() { return question; } + public String getAnswer() { return answer; } + public void setQuestion(String question) { this.question = question; } + public void setAnswer(String answer) { this.answer = answer; } + + // Entity → DTO 변환 + public static QuestionAnswerDto from(QuestionAnswer qa) { + return new QuestionAnswerDto(qa.getQuestion(), qa.getAnswer()); + } + + // DTO → Entity 변환 + public QuestionAnswer toEntity() { + return new QuestionAnswer(this.question, this.answer); + } +} diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java b/src/main/java/com/JoinUs/dp/entity/ClubApplication.java similarity index 97% rename from src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java rename to src/main/java/com/JoinUs/dp/entity/ClubApplication.java index 22b530a..ad744a8 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java +++ b/src/main/java/com/JoinUs/dp/entity/ClubApplication.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubApplication.java -package com.JoinUs.dp.global.domain.application.entity; +package com.JoinUs.dp.entity; import java.time.LocalDate; import java.util.List; diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java b/src/main/java/com/JoinUs/dp/entity/ClubStatus.java similarity index 84% rename from src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java rename to src/main/java/com/JoinUs/dp/entity/ClubStatus.java index a0c5fa6..6fe3cb7 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java +++ b/src/main/java/com/JoinUs/dp/entity/ClubStatus.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/domain/application/entity/ClubStatus.java -package com.JoinUs.dp.global.domain.application.entity; +package com.JoinUs.dp.entity; public enum ClubStatus { PENDING, // 심사중(신규) diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java b/src/main/java/com/JoinUs/dp/entity/Notice.java similarity index 96% rename from src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java rename to src/main/java/com/JoinUs/dp/entity/Notice.java index b20fdac..79004d7 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/notice/entity/Notice.java +++ b/src/main/java/com/JoinUs/dp/entity/Notice.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.global.domain.notice.entity; +package com.JoinUs.dp.entity; import java.time.LocalDateTime; diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java b/src/main/java/com/JoinUs/dp/entity/QuestionAnswer.java similarity index 91% rename from src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java rename to src/main/java/com/JoinUs/dp/entity/QuestionAnswer.java index de8ff86..21a7f60 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java +++ b/src/main/java/com/JoinUs/dp/entity/QuestionAnswer.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/domain/application/entity/QuestionAnswer.java -package com.JoinUs.dp.global.domain.application.entity; +package com.JoinUs.dp.entity; public class QuestionAnswer { private String question; diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java b/src/main/java/com/JoinUs/dp/repository/ClubApplicationRepository.java similarity index 90% rename from src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java rename to src/main/java/com/JoinUs/dp/repository/ClubApplicationRepository.java index 77b88e1..d2077a2 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java +++ b/src/main/java/com/JoinUs/dp/repository/ClubApplicationRepository.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/domain/application/repository/ClubApplicationRepository.java -package com.JoinUs.dp.global.domain.application.repository; +package com.JoinUs.dp.repository; import java.time.LocalDate; import java.util.*; @@ -8,10 +8,10 @@ import org.springframework.stereotype.Repository; -import com.JoinUs.dp.global.domain.application.dto.ClubSummary; -import com.JoinUs.dp.global.domain.application.entity.ClubApplication; -import com.JoinUs.dp.global.domain.application.entity.ClubStatus; -import com.JoinUs.dp.global.domain.application.entity.QuestionAnswer; +import com.JoinUs.dp.dto.ClubSummary; +import com.JoinUs.dp.entity.ClubApplication; +import com.JoinUs.dp.entity.ClubStatus; +import com.JoinUs.dp.entity.QuestionAnswer; @Repository public class ClubApplicationRepository { diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java b/src/main/java/com/JoinUs/dp/repository/NoticeRepository.java similarity index 94% rename from src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java rename to src/main/java/com/JoinUs/dp/repository/NoticeRepository.java index a583ef2..8f3f2a0 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/notice/repository/NoticeRepository.java +++ b/src/main/java/com/JoinUs/dp/repository/NoticeRepository.java @@ -1,9 +1,9 @@ -package com.JoinUs.dp.global.domain.notice.repository; +package com.JoinUs.dp.repository; import org.springframework.stereotype.Repository; -import com.JoinUs.dp.global.domain.notice.entity.Notice; -import com.JoinUs.dp.global.domain.notice.entity.Notice.Type; +import com.JoinUs.dp.entity.Notice; +import com.JoinUs.dp.entity.Notice.Type; import java.time.LocalDateTime; import java.util.*; diff --git a/src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java b/src/main/java/com/JoinUs/dp/service/ClubApplicationService.java similarity index 93% rename from src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java rename to src/main/java/com/JoinUs/dp/service/ClubApplicationService.java index 9b7a9be..6575567 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java +++ b/src/main/java/com/JoinUs/dp/service/ClubApplicationService.java @@ -1,5 +1,5 @@ // src/main/java/com/JoinUs/dp/global/domain/application/service/ClubApplicationService.java -package com.JoinUs.dp.global.domain.application.service; +package com.JoinUs.dp.service; import java.time.LocalDate; import java.util.List; @@ -8,12 +8,12 @@ import org.springframework.stereotype.Service; // import org.springframework.transaction.annotation.Transactional; // ← 트랜잭션이 필요하면 주석 해제 -import com.JoinUs.dp.global.common.exception.NotFoundException; -import com.JoinUs.dp.global.domain.application.dto.ClubApplicationDto; -import com.JoinUs.dp.global.domain.application.dto.ClubSummary; -import com.JoinUs.dp.global.domain.application.entity.ClubApplication; -import com.JoinUs.dp.global.domain.application.entity.ClubStatus; -import com.JoinUs.dp.global.domain.application.repository.ClubApplicationRepository; +import com.JoinUs.dp.common.exception.NotFoundException; +import com.JoinUs.dp.dto.ClubApplicationDto; +import com.JoinUs.dp.dto.ClubSummary; +import com.JoinUs.dp.entity.ClubApplication; +import com.JoinUs.dp.entity.ClubStatus; +import com.JoinUs.dp.repository.ClubApplicationRepository; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java b/src/main/java/com/JoinUs/dp/service/NoticeService.java similarity index 84% rename from src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java rename to src/main/java/com/JoinUs/dp/service/NoticeService.java index 1732d33..7c3acb9 100644 --- a/src/main/java/com/JoinUs/dp/global/domain/notice/service/NoticeService.java +++ b/src/main/java/com/JoinUs/dp/service/NoticeService.java @@ -1,9 +1,10 @@ -package com.JoinUs.dp.global.domain.notice.service; +package com.JoinUs.dp.service; + +import com.JoinUs.dp.common.exception.NotFoundException; +import com.JoinUs.dp.dto.NoticeResponse; +import com.JoinUs.dp.entity.Notice; +import com.JoinUs.dp.repository.NoticeRepository; -import com.JoinUs.dp.global.common.exception.NotFoundException; -import com.JoinUs.dp.global.domain.notice.dto.NoticeResponse; -import com.JoinUs.dp.global.domain.notice.entity.Notice; -import com.JoinUs.dp.global.domain.notice.repository.NoticeRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/test/java/com/JoinUs/dp/DpApplicationTests.java b/src/test/java/com/JoinUs/dp/DpApplicationTests.java deleted file mode 100644 index 6db4fc6..0000000 --- a/src/test/java/com/JoinUs/dp/DpApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.JoinUs.dp; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class DpApplicationTests { - - @Test - void contextLoads() { - } - -} From 60e0f86918d5ec0c4dab89b47d332e854687d034 Mon Sep 17 00:00:00 2001 From: kjinhub Date: Tue, 23 Sep 2025 12:50:10 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix=20:=20gladle=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?config=ED=8C=A8=ED=82=A4=EC=A7=80=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 --- build.gradle | 14 ++++++++++++++ .../{ => global}/config/SecurityConfiguration.java | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) rename src/main/java/com/JoinUs/dp/{ => global}/config/SecurityConfiguration.java (98%) diff --git a/build.gradle b/build.gradle index 4765ffa..2748399 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,21 @@ configurations { repositories { mavenCentral() } + dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-websocket' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.h2database:h2' +// runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // Spring Boot Starters implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' diff --git a/src/main/java/com/JoinUs/dp/config/SecurityConfiguration.java b/src/main/java/com/JoinUs/dp/global/config/SecurityConfiguration.java similarity index 98% rename from src/main/java/com/JoinUs/dp/config/SecurityConfiguration.java rename to src/main/java/com/JoinUs/dp/global/config/SecurityConfiguration.java index bf28a1c..dc231ed 100644 --- a/src/main/java/com/JoinUs/dp/config/SecurityConfiguration.java +++ b/src/main/java/com/JoinUs/dp/global/config/SecurityConfiguration.java @@ -1,4 +1,4 @@ -package com.JoinUs.dp.config; +package com.JoinUs.dp.global.config; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; From 7bb43c30fd953f50c982031da048b4ddfc3fb226 Mon Sep 17 00:00:00 2001 From: kjinhub Date: Fri, 17 Oct 2025 16:30:28 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=EB=AA=85=EC=8B=9C=EC=A0=81=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EC=82=AC=EC=9A=A9=20->=20Lombok=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/JoinUs/dp/controller/ClubApplicationController.java | 4 +--- src/main/java/com/JoinUs/dp/controller/NoticeController.java | 5 +---- .../java/com/JoinUs/dp/service/ClubApplicationService.java | 5 +---- src/main/java/com/JoinUs/dp/service/NoticeService.java | 5 +---- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java b/src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java index 1cf30a9..002ab40 100644 --- a/src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java +++ b/src/main/java/com/JoinUs/dp/controller/ClubApplicationController.java @@ -21,9 +21,7 @@ public class ClubApplicationController { private final ClubApplicationService service; - public ClubApplicationController(ClubApplicationService service) { - this.service = service; - } + // 동아리 신청 등록: POST /api/applications @PostMapping(ApiPath.APPLICATIONS) diff --git a/src/main/java/com/JoinUs/dp/controller/NoticeController.java b/src/main/java/com/JoinUs/dp/controller/NoticeController.java index 6e7a7aa..7548b11 100644 --- a/src/main/java/com/JoinUs/dp/controller/NoticeController.java +++ b/src/main/java/com/JoinUs/dp/controller/NoticeController.java @@ -21,10 +21,7 @@ public class NoticeController { private final NoticeService noticeService; // ✅ new 금지, DI 받기 - // ✅ 명시적 생성자 주입 - public NoticeController(NoticeService noticeService) { - this.noticeService = noticeService; - } + @GetMapping public ResponseEntity>> getFaqs() { diff --git a/src/main/java/com/JoinUs/dp/service/ClubApplicationService.java b/src/main/java/com/JoinUs/dp/service/ClubApplicationService.java index 6575567..207611d 100644 --- a/src/main/java/com/JoinUs/dp/service/ClubApplicationService.java +++ b/src/main/java/com/JoinUs/dp/service/ClubApplicationService.java @@ -23,10 +23,7 @@ public class ClubApplicationService { // ✅ 잘못된 자기 주입 제거, Repository 주입으로 수정 private final ClubApplicationRepository repository; - public ClubApplicationService(ClubApplicationRepository repository) { - this.repository = repository; - } - + // -------------------- 기본 CRUD -------------------- // @Transactional diff --git a/src/main/java/com/JoinUs/dp/service/NoticeService.java b/src/main/java/com/JoinUs/dp/service/NoticeService.java index 7c3acb9..e465b10 100644 --- a/src/main/java/com/JoinUs/dp/service/NoticeService.java +++ b/src/main/java/com/JoinUs/dp/service/NoticeService.java @@ -15,10 +15,7 @@ public class NoticeService { private final NoticeRepository noticeRepository; - // ✅ 명시적 생성자 주입 - public NoticeService(NoticeRepository noticeRepository) { - this.noticeRepository = noticeRepository; - } + // FAQ 목록 public List getFaqList() { return noticeRepository.findAllFaq().stream()