From fa3f5b5d6a346ec768ae1e4c325ea07da3af8993 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:35:51 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=EB=B0=B1=EC=97=94=EB=93=9C,=20?= =?UTF-8?q?=ED=94=84=EB=A1=A0=ED=8A=B8=EC=97=94=EB=93=9C=20=EC=A7=81?= =?UTF-8?q?=EA=B5=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존 개발 직군은 호환성을 위해 유지 --- src/main/kotlin/land/leets/domain/application/type/Position.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/land/leets/domain/application/type/Position.kt b/src/main/kotlin/land/leets/domain/application/type/Position.kt index 676640a..0405e42 100644 --- a/src/main/kotlin/land/leets/domain/application/type/Position.kt +++ b/src/main/kotlin/land/leets/domain/application/type/Position.kt @@ -2,6 +2,8 @@ package land.leets.domain.application.type enum class Position(val statusKo: String) { DEV("개발"), + FRONTEND("프론트엔드"), + BACKEND("백엔드"), DESIGN("디자인"), BX_BI("BX/BI"), UX_UI("UX/UI"), From 79f227bd250c31ea9e6ac72d05e95c1d50138b95 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:36:10 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/TemporaryApplication.kt | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/domain/TemporaryApplication.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/domain/TemporaryApplication.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/domain/TemporaryApplication.kt new file mode 100644 index 0000000..e0147ff --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/domain/TemporaryApplication.kt @@ -0,0 +1,112 @@ +package land.leets.domain.temporaryApplication.domain + +import jakarta.persistence.* +import land.leets.domain.application.type.Position +import land.leets.domain.shared.BaseTimeEntity +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationRequest +import land.leets.domain.user.domain.User + +@Entity(name = "temporary_applications") +class TemporaryApplication( + + @OneToOne + @JoinColumn(name = "user_id") + var user: User, + + @Column + var name: String? = null, + + @Column + var phone: String? = null, + + @Column + var major: String? = null, + + @Column + var grade: String? = null, + + @Column + var project: String? = null, + + @Column + var algorithm: String? = null, + + @Column + var portfolio: String? = null, + + @Column(columnDefinition = "char(10)") + @Enumerated(EnumType.STRING) + var position: Position, + + @Column + var career: String? = null, + + @Column + var interviewDay: String? = null, + + @Column + var interviewTime: String? = null, + + @Column(columnDefinition = "text(600)") + var motive: String? = null, + + @Column(columnDefinition = "text(600)") + var expectation: String? = null, + + @Column(columnDefinition = "text(600)") + var capability: String? = null, + + @Column(columnDefinition = "text(600)") + var conflict: String? = null, + + @Column(columnDefinition = "text(600)") + var passion: String? = null, + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null +) : BaseTimeEntity() { + + fun updateContent(request: TemporaryApplicationRequest) { + name = request.name + phone = request.phone + major = request.major + grade = request.grade + project = request.project + algorithm = request.algorithm + portfolio = request.portfolio + position = request.position + career = request.career + interviewDay = request.interviewDay + interviewTime = request.interviewTime + motive = request.motive + expectation = request.expectation + capability = request.capability + conflict = request.conflict + passion = request.passion + } + + companion object { + fun of(user: User, request: TemporaryApplicationRequest): TemporaryApplication { + return TemporaryApplication( + user = user, + name = request.name, + phone = request.phone, + major = request.major, + grade = request.grade, + project = request.project, + algorithm = request.algorithm, + portfolio = request.portfolio, + position = request.position, + career = request.career, + interviewDay = request.interviewDay, + interviewTime = request.interviewTime, + motive = request.motive, + expectation = request.expectation, + capability = request.capability, + conflict = request.conflict, + passion = request.passion + ) + } + } +} From 4a4b3d98522d04db71919d8f5bf10318112cf67f Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:36:20 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20JPA=20Repository=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/TemporaryApplicationRepository.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/domain/repository/TemporaryApplicationRepository.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/domain/repository/TemporaryApplicationRepository.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/domain/repository/TemporaryApplicationRepository.kt new file mode 100644 index 0000000..cc39c88 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/domain/repository/TemporaryApplicationRepository.kt @@ -0,0 +1,9 @@ +package land.leets.domain.temporaryApplication.domain.repository + +import land.leets.domain.temporaryApplication.domain.TemporaryApplication +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + +interface TemporaryApplicationRepository : JpaRepository { + fun findByUser_Id(id: UUID): TemporaryApplication? +} From 9ab45ca4e883f81e149f5e6450e007fc5d627a47 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:36:39 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=83=9D=EC=84=B1=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/TemporaryApplicationRequest.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationRequest.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationRequest.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationRequest.kt new file mode 100644 index 0000000..8a28692 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationRequest.kt @@ -0,0 +1,23 @@ +package land.leets.domain.temporaryApplication.presentation.dto + +import land.leets.domain.application.type.Position + +data class TemporaryApplicationRequest( + val name: String?, + val sid: String?, + val phone: String?, + val major: String?, + val grade: String?, + val project: String?, + val algorithm: String?, + val portfolio: String?, + val position: Position, + val career: String?, + val interviewDay: String?, + val interviewTime: String?, + val motive: String?, + val expectation: String?, + val capability: String?, + val conflict: String?, + val passion: String? +) From 63ba66ae874ee8f66bdec13d52cb004989c939ab Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:36:47 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=9D=91=EB=8B=B5=20DTO=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/TemporaryApplicationResponse.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationResponse.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationResponse.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationResponse.kt new file mode 100644 index 0000000..c61351b --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/dto/TemporaryApplicationResponse.kt @@ -0,0 +1,46 @@ +package land.leets.domain.temporaryApplication.presentation.dto + +import land.leets.domain.application.type.Position +import land.leets.domain.temporaryApplication.domain.TemporaryApplication + +data class TemporaryApplicationResponse( + val name: String?, + val phone: String?, + val major: String?, + val grade: String?, + val project: String?, + val algorithm: String?, + val portfolio: String?, + val position: Position, + val career: String?, + val interviewDay: String?, + val interviewTime: String?, + val motive: String?, + val expectation: String?, + val capability: String?, + val conflict: String?, + val passion: String? +) { + companion object { + fun from(temporaryApplication: TemporaryApplication): TemporaryApplicationResponse { + return TemporaryApplicationResponse( + name = temporaryApplication.name, + phone = temporaryApplication.phone, + major = temporaryApplication.major, + grade = temporaryApplication.grade, + project = temporaryApplication.project, + algorithm = temporaryApplication.algorithm, + portfolio = temporaryApplication.portfolio, + position = temporaryApplication.position, + career = temporaryApplication.career, + interviewDay = temporaryApplication.interviewDay, + interviewTime = temporaryApplication.interviewTime, + motive = temporaryApplication.motive, + expectation = temporaryApplication.expectation, + capability = temporaryApplication.capability, + conflict = temporaryApplication.conflict, + passion = temporaryApplication.passion + ) + } + } +} From 57f5b15e18490084d99fc216ecd28abd76907a75 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:36:58 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/GetTemporaryApplication.kt | 8 ++++++++ .../usecase/GetTemporaryApplicationImpl.kt | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplication.kt create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImpl.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplication.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplication.kt new file mode 100644 index 0000000..fe78841 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplication.kt @@ -0,0 +1,8 @@ +package land.leets.domain.temporaryApplication.usecase + +import land.leets.domain.auth.AuthDetails +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationResponse + +interface GetTemporaryApplication { + fun execute(authDetails: AuthDetails): TemporaryApplicationResponse? +} diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImpl.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImpl.kt new file mode 100644 index 0000000..2b857a0 --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImpl.kt @@ -0,0 +1,17 @@ +package land.leets.domain.temporaryApplication.usecase + +import land.leets.domain.auth.AuthDetails +import land.leets.domain.temporaryApplication.domain.repository.TemporaryApplicationRepository +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationResponse +import org.springframework.stereotype.Service + +@Service +class GetTemporaryApplicationImpl( + private val temporaryApplicationRepository: TemporaryApplicationRepository +) : GetTemporaryApplication { + + override fun execute(authDetails: AuthDetails): TemporaryApplicationResponse? { + return temporaryApplicationRepository.findByUser_Id(authDetails.uid) + ?.let { TemporaryApplicationResponse.from(it) } + } +} From 71f621d1a84073e285921bceba33f903096833c0 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:37:09 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/SaveTemporaryApplication.kt | 9 +++++ .../usecase/SaveTemporaryApplicationImpl.kt | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplication.kt create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImpl.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplication.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplication.kt new file mode 100644 index 0000000..a6941de --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplication.kt @@ -0,0 +1,9 @@ +package land.leets.domain.temporaryApplication.usecase + +import land.leets.domain.auth.AuthDetails +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationRequest +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationResponse + +interface SaveTemporaryApplication { + fun execute(authDetails: AuthDetails, request: TemporaryApplicationRequest): TemporaryApplicationResponse +} diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImpl.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImpl.kt new file mode 100644 index 0000000..1d34a2a --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImpl.kt @@ -0,0 +1,34 @@ +package land.leets.domain.temporaryApplication.usecase + +import land.leets.domain.auth.AuthDetails +import land.leets.domain.temporaryApplication.domain.TemporaryApplication +import land.leets.domain.temporaryApplication.domain.repository.TemporaryApplicationRepository +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationRequest +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationResponse +import land.leets.domain.user.domain.repository.UserRepository +import land.leets.domain.user.exception.UserNotFoundException +import org.springframework.stereotype.Service + +@Service +class SaveTemporaryApplicationImpl( + private val temporaryApplicationRepository: TemporaryApplicationRepository, + private val userRepository: UserRepository, +) : SaveTemporaryApplication { + + override fun execute(authDetails: AuthDetails, request: TemporaryApplicationRequest): TemporaryApplicationResponse { + val user = userRepository.findById(authDetails.uid).orElseThrow { UserNotFoundException() } + + val temporaryApplication = temporaryApplicationRepository.findByUser_Id(user.id!!) + ?.also { it.updateContent(request) } + ?: TemporaryApplication.of(user, request) + + request.phone?.let { phone -> + user.updateUserInfo(request.sid, phone) + userRepository.save(user) + } + + return TemporaryApplicationResponse.from( + temporaryApplicationRepository.save(temporaryApplication) + ) + } +} From e8e3e45355aa6e2208f5540fd952dd8e13563c9a Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:39:10 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20API=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TemporaryApplicationController.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt new file mode 100644 index 0000000..2e2214f --- /dev/null +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt @@ -0,0 +1,52 @@ +package land.leets.domain.temporaryApplication.presentation + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.responses.ApiResponses +import land.leets.domain.auth.AuthDetails +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationRequest +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationResponse +import land.leets.domain.temporaryApplication.usecase.GetTemporaryApplication +import land.leets.domain.temporaryApplication.usecase.SaveTemporaryApplication +import land.leets.global.error.ErrorResponse +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/temporary-application") +class TemporaryApplicationController( + private val getTemporaryApplication: GetTemporaryApplication, + private val saveTemporaryApplication: SaveTemporaryApplication +) { + + @Operation(summary = "(유저) 임시저장 지원서 조회", description = "임시 저장한 지원서를 호출합니다.") + @ApiResponses( + ApiResponse(responseCode = "200"), + ApiResponse(responseCode = "400", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "403", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "404", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "500", content = [Content(schema = Schema(implementation = ErrorResponse::class))]) + ) + @GetMapping + fun get( + @AuthenticationPrincipal authDetails: AuthDetails + ): TemporaryApplicationResponse? = + getTemporaryApplication.execute(authDetails) + + @Operation(summary = "(유저) 지원서 임시 저장", description = "지원서를 작성합니다.") + @ApiResponses( + ApiResponse(responseCode = "200"), + ApiResponse(responseCode = "400", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "403", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "404", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), + ApiResponse(responseCode = "500", content = [Content(schema = Schema(implementation = ErrorResponse::class))]) + ) + @PatchMapping + fun save( + @AuthenticationPrincipal authDetails: AuthDetails, + @RequestBody request: TemporaryApplicationRequest + ): TemporaryApplicationResponse = + saveTemporaryApplication.execute(authDetails, request) +} From 232c86bfe2329f50ddd0fe4ba3628650d607aa42 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:40:02 +0900 Subject: [PATCH 09/16] =?UTF-8?q?refactor:=20=EC=A7=80=EC=9B=90=EC=84=9C?= =?UTF-8?q?=20=EC=A0=9C=EC=B6=9C=20=EC=8B=9C=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=EC=84=9C=EB=A5=BC=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/usecase/CreateApplicationImpl.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/land/leets/domain/application/usecase/CreateApplicationImpl.kt b/src/main/kotlin/land/leets/domain/application/usecase/CreateApplicationImpl.kt index c55248e..92a66e2 100644 --- a/src/main/kotlin/land/leets/domain/application/usecase/CreateApplicationImpl.kt +++ b/src/main/kotlin/land/leets/domain/application/usecase/CreateApplicationImpl.kt @@ -6,6 +6,7 @@ import land.leets.domain.application.exception.ApplicationAlreadyExistsException import land.leets.domain.application.presentation.dto.ApplicationRequest import land.leets.domain.application.type.SubmitStatus import land.leets.domain.auth.AuthDetails +import land.leets.domain.temporaryApplication.domain.repository.TemporaryApplicationRepository import land.leets.domain.user.domain.User import land.leets.domain.user.domain.repository.UserRepository import land.leets.domain.user.exception.UserNotFoundException @@ -15,7 +16,8 @@ import java.time.LocalDateTime @Service class CreateApplicationImpl( private val applicationRepository: ApplicationRepository, - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val temporaryApplicationRepository: TemporaryApplicationRepository ) : CreateApplication { override fun execute(authDetails: AuthDetails, request: ApplicationRequest): Application { @@ -48,6 +50,12 @@ class CreateApplicationImpl( submitStatus = request.submitStatus, appliedAt = if (request.submitStatus == SubmitStatus.SUBMIT) LocalDateTime.now() else null ) - return applicationRepository.save(application) + val savedApplication = applicationRepository.save(application) + + temporaryApplicationRepository.findByUser_Id(user.id)?.let { + temporaryApplicationRepository.delete(it) + } + + return savedApplication } } From 7b814e077e353a04305fdc1f93940d0893510efd Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:40:25 +0900 Subject: [PATCH 10/16] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=EC=84=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20`@Deprecated`=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../land/leets/domain/application/usecase/UpdateApplication.kt | 1 + .../leets/domain/application/usecase/UpdateApplicationImpl.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplication.kt b/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplication.kt index f26783d..ae0e394 100644 --- a/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplication.kt +++ b/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplication.kt @@ -4,6 +4,7 @@ import land.leets.domain.application.domain.Application import land.leets.domain.application.presentation.dto.ApplicationRequest import land.leets.domain.auth.AuthDetails +@Deprecated("임시 저장 테이블 분리에 따른 미사용") interface UpdateApplication { fun execute(authDetails: AuthDetails, request: ApplicationRequest): Application } diff --git a/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplicationImpl.kt b/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplicationImpl.kt index a40e7e1..95c521b 100644 --- a/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplicationImpl.kt +++ b/src/main/kotlin/land/leets/domain/application/usecase/UpdateApplicationImpl.kt @@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime @Service +@Deprecated("임시 저장 테이블 분리에 따른 미사용") class UpdateApplicationImpl( private val applicationRepository: ApplicationRepository, private val updateUser: UpdateUser From 4899faf53c1c84b8d4596618a7304f173316e7cc Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:41:27 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20API=20=EC=97=94=ED=8A=B8=ED=8F=AC=EC=9D=B8?= =?UTF-8?q?=ED=8A=B8=20=ED=97=88=EC=9A=A9=EB=B2=94=EC=9C=84=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/land/leets/global/config/SecurityConfig.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/land/leets/global/config/SecurityConfig.kt b/src/main/kotlin/land/leets/global/config/SecurityConfig.kt index cca7061..254a533 100644 --- a/src/main/kotlin/land/leets/global/config/SecurityConfig.kt +++ b/src/main/kotlin/land/leets/global/config/SecurityConfig.kt @@ -82,6 +82,10 @@ class SecurityConfig( authorize(HttpMethod.GET, "/application/{id}", hasAuthority(AuthRole.ROLE_ADMIN.role)) authorize(HttpMethod.PATCH, "/application/{id}", hasAuthority(AuthRole.ROLE_ADMIN.role)) + // temporary applications + authorize(HttpMethod.GET, "/temporary-application", hasAuthority(AuthRole.ROLE_USER.role)) + authorize(HttpMethod.PATCH, "/temporary-application", hasAuthority(AuthRole.ROLE_USER.role)) + // interviews authorize(HttpMethod.PATCH, "/interview", hasAuthority(AuthRole.ROLE_USER.role)) authorize(HttpMethod.POST, "/interview/{id}", hasAuthority(AuthRole.ROLE_ADMIN.role)) From 5bd8453aed3b21b3d7633398b83bbb9ef652c6d7 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:43:27 +0900 Subject: [PATCH 12/16] =?UTF-8?q?test:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GetTemporaryApplicationImplTest.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/test/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImplTest.kt diff --git a/src/test/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImplTest.kt b/src/test/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImplTest.kt new file mode 100644 index 0000000..60dba9d --- /dev/null +++ b/src/test/kotlin/land/leets/domain/temporaryApplication/usecase/GetTemporaryApplicationImplTest.kt @@ -0,0 +1,42 @@ +package land.leets.domain.temporaryApplication.usecase + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import land.leets.domain.auth.AuthDetails +import land.leets.domain.shared.AuthRole +import land.leets.domain.temporaryApplication.domain.TemporaryApplication +import land.leets.domain.temporaryApplication.domain.repository.TemporaryApplicationRepository +import java.util.* + +class GetTemporaryApplicationImplTest : DescribeSpec({ + + val temporaryApplicationRepository = mockk() + val getTemporaryApplication = GetTemporaryApplicationImpl(temporaryApplicationRepository) + + describe("GetTemporaryApplicationImpl") { + context("임시 저장된 지원서를 조회할 때") { + val uid = UUID.randomUUID() + val authDetails = AuthDetails(uid, "test@test.com", AuthRole.ROLE_USER) + val temporaryApplication = mockk(relaxed = true) + + it("지원서가 존재하면 Response를 반환한다") { + every { temporaryApplicationRepository.findByUser_Id(uid) } returns temporaryApplication + every { temporaryApplication.name } returns "Test" + + val response = getTemporaryApplication.execute(authDetails) + + response?.name shouldBe "Test" + } + + it("지원서가 존재하지 않으면 null을 반환한다") { + every { temporaryApplicationRepository.findByUser_Id(uid) } returns null + + val response = getTemporaryApplication.execute(authDetails) + + response shouldBe null + } + } + } +}) From 8b46d1f34845745a53b74377951079a088934356 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:43:36 +0900 Subject: [PATCH 13/16] =?UTF-8?q?test:=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SaveTemporaryApplicationImplTest.kt | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/test/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImplTest.kt diff --git a/src/test/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImplTest.kt b/src/test/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImplTest.kt new file mode 100644 index 0000000..e78ed9c --- /dev/null +++ b/src/test/kotlin/land/leets/domain/temporaryApplication/usecase/SaveTemporaryApplicationImplTest.kt @@ -0,0 +1,104 @@ +package land.leets.domain.temporaryApplication.usecase + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import land.leets.domain.application.type.Position +import land.leets.domain.auth.AuthDetails +import land.leets.domain.shared.AuthRole +import land.leets.domain.temporaryApplication.domain.TemporaryApplication +import land.leets.domain.temporaryApplication.domain.repository.TemporaryApplicationRepository +import land.leets.domain.temporaryApplication.presentation.dto.TemporaryApplicationRequest +import land.leets.domain.user.domain.User +import land.leets.domain.user.domain.repository.UserRepository +import land.leets.domain.user.exception.UserNotFoundException +import java.util.* + +class SaveTemporaryApplicationImplTest : DescribeSpec({ + + val temporaryApplicationRepository = mockk() + val userRepository = mockk() + val saveTemporaryApplication = SaveTemporaryApplicationImpl(temporaryApplicationRepository, userRepository) + + describe("SaveTemporaryApplicationImpl") { + context("지원서를 임시 저장할 때") { + val uid = UUID.randomUUID() + val authDetails = AuthDetails(uid, "test@test.com", AuthRole.ROLE_USER) + val request = TemporaryApplicationRequest( + name = "Test", + sid = "20202020", + phone = "010-1234-5678", + major = "CS", + grade = "4", + project = "Project", + algorithm = "Algo", + portfolio = "Port", + position = Position.DEV, + career = "Career", + interviewDay = "Monday", + interviewTime = "10:00", + motive = "Motive", + expectation = "Expectation", + capability = "Capability", + conflict = "Conflict", + passion = "Passion" + ) + val user = User( + sid = "20202020", + name = "Test", + phone = "010-1234-5678", + email = "test@test.com", + sub = "google-oauth2|123456", + id = uid + ) + + it("기존 지원서가 없으면 새로 생성한다") { + every { userRepository.findById(uid) } returns Optional.of(user) + every { temporaryApplicationRepository.findByUser_Id(uid) } returns null + every { temporaryApplicationRepository.save(any()) } returnsArgument 0 + every { userRepository.save(any()) } returns user + + val response = saveTemporaryApplication.execute(authDetails, request) + + response.name shouldBe request.name + verify { temporaryApplicationRepository.save(any()) } + } + + it("기존 지원서가 있으면 수정한다") { + val existingApplication = mockk(relaxed = true) + every { userRepository.findById(uid) } returns Optional.of(user) + every { temporaryApplicationRepository.findByUser_Id(uid) } returns existingApplication + every { temporaryApplicationRepository.save(any()) } returnsArgument 0 + every { userRepository.save(any()) } returns user + every { existingApplication.name } returns "Updated Name" + + saveTemporaryApplication.execute(authDetails, request) + + verify { existingApplication.updateContent(request) } + verify { temporaryApplicationRepository.save(existingApplication) } + } + + it("전화번호가 있으면 유저 정보를 업데이트한다") { + every { userRepository.findById(uid) } returns Optional.of(user) + every { temporaryApplicationRepository.findByUser_Id(uid) } returns null + every { temporaryApplicationRepository.save(any()) } returnsArgument 0 + every { userRepository.save(any()) } returns user + + saveTemporaryApplication.execute(authDetails, request) + + verify { userRepository.save(user) } + } + + it("유저를 찾을 수 없으면 UserNotFoundException을 던진다") { + every { userRepository.findById(uid) } returns Optional.empty() + + shouldThrow { + saveTemporaryApplication.execute(authDetails, request) + } + } + } + } +}) From 7dc5a5981d16f4e0a153ca8e662ee9fb3c22a9dc Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:44:08 +0900 Subject: [PATCH 14/16] =?UTF-8?q?test:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=20=EC=8B=9C=20=EC=9E=84=EC=8B=9C=20=EC=A7=80?= =?UTF-8?q?=EC=9B=90=EC=84=9C=EB=A5=BC=20=EC=82=AD=EC=A0=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/CreateApplicationImplTest.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/land/leets/domain/application/usecase/CreateApplicationImplTest.kt b/src/test/kotlin/land/leets/domain/application/usecase/CreateApplicationImplTest.kt index 86665b1..d2e48a5 100644 --- a/src/test/kotlin/land/leets/domain/application/usecase/CreateApplicationImplTest.kt +++ b/src/test/kotlin/land/leets/domain/application/usecase/CreateApplicationImplTest.kt @@ -14,6 +14,8 @@ import land.leets.domain.application.type.Position import land.leets.domain.application.type.SubmitStatus import land.leets.domain.auth.AuthDetails import land.leets.domain.shared.AuthRole +import land.leets.domain.temporaryApplication.domain.TemporaryApplication +import land.leets.domain.temporaryApplication.domain.repository.TemporaryApplicationRepository import land.leets.domain.user.domain.User import land.leets.domain.user.domain.repository.UserRepository import java.util.* @@ -21,8 +23,9 @@ import java.util.* class CreateApplicationImplTest : DescribeSpec({ val applicationRepository = mockk() + val temporaryApplicationRepository = mockk() val userRepository = mockk() - val createApplication = CreateApplicationImpl(applicationRepository, userRepository) + val createApplication = CreateApplicationImpl(applicationRepository, userRepository, temporaryApplicationRepository) describe("CreateApplicationImpl 유스케이스는") { context("지원서 생성을 요청할 때") { @@ -62,6 +65,7 @@ class CreateApplicationImplTest : DescribeSpec({ every { applicationRepository.findByUser_Id(uid) } returns null every { userRepository.save(any()) } returns user every { applicationRepository.save(any()) } returnsArgument 0 + every { temporaryApplicationRepository.findByUser_Id(uid) } returns null val result = createApplication.execute(authDetails, request) @@ -70,6 +74,21 @@ class CreateApplicationImplTest : DescribeSpec({ verify { applicationRepository.save(any()) } } + it("임시 저장 지원서가 있으면 삭제한다") { + val temporaryApplication = mockk() + every { userRepository.findById(uid) } returns Optional.of(user) + every { applicationRepository.findByUser_Id(uid) } returns null + every { userRepository.save(any()) } returns user + every { applicationRepository.save(any()) } returnsArgument 0 + every { temporaryApplicationRepository.findByUser_Id(uid) } returns temporaryApplication + every { temporaryApplicationRepository.delete(temporaryApplication) } returns Unit + + val result = createApplication.execute(authDetails, request) + + result.name shouldBe request.name + verify { temporaryApplicationRepository.delete(temporaryApplication) } + } + it("이미 지원서가 존재하면 ApplicationAlreadyExistsException을 던진다") { val application = mockk() From 8d2442643efa266f12c6ecb75a7d3d2c86e0664c Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:46:09 +0900 Subject: [PATCH 15/16] =?UTF-8?q?docs:=20Swagger=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EC=97=90=20=EA=B8=B0=EC=A1=B4=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EB=AF=B8=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EB=AA=85=EC=84=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/application/presentation/ApplicationController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt b/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt index d7d8c71..8173b96 100644 --- a/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt +++ b/src/main/kotlin/land/leets/domain/application/presentation/ApplicationController.kt @@ -45,7 +45,7 @@ class ApplicationController( return createApplication.execute(authDetails, request) } - @Operation(summary = "(유저) 지원서 수정", description = "지원서를 수정합니다.") + @Operation(summary = "(유저) 지원서 수정 (미사용)", description = "지원서를 수정합니다. (미사용)") @ApiResponses( ApiResponse(responseCode = "200"), ApiResponse(responseCode = "400", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), From 8c780fb42b775e8e2b446b0f16bea48a33408fa3 Mon Sep 17 00:00:00 2001 From: yechan-kim <60172300+yechan-kim@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:57:20 +0900 Subject: [PATCH 16/16] =?UTF-8?q?refactor:=20http=20method=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=98=EB=8F=84=EC=97=90=20=EB=A7=9E=EA=B2=8C=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 --- .../presentation/TemporaryApplicationController.kt | 2 +- src/main/kotlin/land/leets/global/config/SecurityConfig.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt index 2e2214f..03716aa 100644 --- a/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt +++ b/src/main/kotlin/land/leets/domain/temporaryApplication/presentation/TemporaryApplicationController.kt @@ -43,7 +43,7 @@ class TemporaryApplicationController( ApiResponse(responseCode = "404", content = [Content(schema = Schema(implementation = ErrorResponse::class))]), ApiResponse(responseCode = "500", content = [Content(schema = Schema(implementation = ErrorResponse::class))]) ) - @PatchMapping + @PutMapping fun save( @AuthenticationPrincipal authDetails: AuthDetails, @RequestBody request: TemporaryApplicationRequest diff --git a/src/main/kotlin/land/leets/global/config/SecurityConfig.kt b/src/main/kotlin/land/leets/global/config/SecurityConfig.kt index 254a533..34e454a 100644 --- a/src/main/kotlin/land/leets/global/config/SecurityConfig.kt +++ b/src/main/kotlin/land/leets/global/config/SecurityConfig.kt @@ -84,7 +84,7 @@ class SecurityConfig( // temporary applications authorize(HttpMethod.GET, "/temporary-application", hasAuthority(AuthRole.ROLE_USER.role)) - authorize(HttpMethod.PATCH, "/temporary-application", hasAuthority(AuthRole.ROLE_USER.role)) + authorize(HttpMethod.PUT, "/temporary-application", hasAuthority(AuthRole.ROLE_USER.role)) // interviews authorize(HttpMethod.PATCH, "/interview", hasAuthority(AuthRole.ROLE_USER.role))