Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
51450a3
chore ( #9 ) : build-logic 삭제
coehgns Jul 11, 2025
d014d3a
build ( #9 ) : DependencyVersion.kt
coehgns Jul 11, 2025
7439092
build ( #9 ) : Dependencies.kt
coehgns Jul 11, 2025
20885c3
build ( #9 ) : PluginVersion
coehgns Jul 11, 2025
8174221
build ( #9 ) : Plugin
coehgns Jul 11, 2025
2016fc9
build ( #9 ) : build.gradle.kts - buildSrc
coehgns Jul 11, 2025
1eecb7b
build ( #9 ) : settings.gradle.kts에서 build-logic 삭제
coehgns Jul 11, 2025
2b8e7ca
chore ( #7 ) : .editorconfig
coehgns Jul 11, 2025
2fe0fe0
feat ( #7 ) : Equus-Feed 레거시 코드 적용
coehgns Jul 11, 2025
050f5c1
chore ( #14 ) : casper-feed 모듈 분리
coehgns Jul 29, 2025
72e3c5f
chore ( #14 ) : casper-feed 모듈 분리
coehgns Jul 29, 2025
7daf22e
feat ( #8 ) : CreateAttachFileResponse
coehgns Jul 29, 2025
0188476
feat ( #8 ) : AttachFileWebAdapter
coehgns Jul 29, 2025
b62362f
feat ( #8 ) : AttachFileJpaEntity
coehgns Jul 29, 2025
19d4ce8
feat ( #8 ) : AttachFileMapper
coehgns Jul 29, 2025
702c7a0
feat ( #8 ) : AttachFileRepository
coehgns Jul 29, 2025
871eacd
feat ( #8 ) : AttachFilePersistenceAdapter
coehgns Jul 29, 2025
84e0d92
feat ( #8 ) : CreateAttachFileUseCase
coehgns Jul 29, 2025
dfd95f6
feat ( #8 ) : DeleteAttachFilePort
coehgns Jul 29, 2025
d91050c
feat ( #8 ) : ExistsAttachFilePort
coehgns Jul 29, 2025
046f418
feat ( #8 ) : FindAttachFilePort
coehgns Jul 29, 2025
c4f5291
feat ( #8 ) : SaveAttachFilePort
coehgns Jul 29, 2025
451bf43
feat ( #8 ) : CreateAttachFileService
coehgns Jul 29, 2025
3671837
feat ( #8 ) : AttachFileNotFoundException
coehgns Jul 29, 2025
faa3ac1
feat ( #8 ) : NoticeNotFoundException
coehgns Jul 29, 2025
00580bf
feat ( #8 ) : CreateNoticeRequest
coehgns Jul 29, 2025
e4543a9
feat ( #8 ) : UpdateNoticeRequest
coehgns Jul 29, 2025
29dd89b
feat ( #8 ) : NoticeResponse
coehgns Jul 29, 2025
864d931
feat ( #8 ) : QueryDetailsNoticeResponse
coehgns Jul 29, 2025
015e354
feat ( #8 ) : QueryListNoticeResponse
coehgns Jul 29, 2025
7c2babe
feat ( #8 ) : QueryNoticeTitleResponse
coehgns Jul 29, 2025
3bcf967
feat ( #8 ) : UploadNoticeImageResponse
coehgns Jul 29, 2025
e7a56ba
feat ( #8 ) : NoticeWebAdapter
coehgns Jul 29, 2025
331abb2
feat ( #8 ) : NoticeJpaEntity
coehgns Jul 29, 2025
cb3de54
feat ( #8 ) : NoticeMapper
coehgns Jul 29, 2025
e0d5d10
feat ( #8 ) : NoticeRepository
coehgns Jul 29, 2025
9614481
feat ( #8 ) : NoticePersistenceAdapter
coehgns Jul 29, 2025
13d4581
feat ( #8 ) : Notice in port 추가
coehgns Jul 29, 2025
82575d3
feat ( #8 ) : Notice out port 추가
coehgns Jul 29, 2025
a272398
feat ( #8 ) : Notice Service 추가
coehgns Jul 29, 2025
1215abe
refactor ( #8 ) : CreateAttachFileResponse data class로 수정
coehgns Jul 29, 2025
731b489
docs ( #8 ) : kdoc
coehgns Jul 29, 2025
6543238
feat ( #8 ) : Notice model 추가
coehgns Jul 29, 2025
545268f
feat ( #8 ) : NoticeType 추가
coehgns Jul 29, 2025
e70ff6a
chore ( #14 ) : 모듈 분리
coehgns Jul 29, 2025
4f9e2d3
docs ( #18 ) : kdoc
coehgns Jul 30, 2025
fb32550
docs ( #8 ) : kdoc
coehgns Jul 30, 2025
4ff4538
Merge remote-tracking branch 'origin/feature/8-layerd-to-hexagonal-no…
Jul 30, 2025
eda9e10
docs ( #8 ) : kdoc
coehgns Jul 30, 2025
10bdfd0
docs ( #8 ) : kdoc
coehgns Jul 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
max_line_length = 120
tab_width = 4
ktlint_disabled_rules = no-wildcard-imports,import-ordering,comment-spacing
11 changes: 0 additions & 11 deletions build-logic/build.gradle.kts

This file was deleted.

17 changes: 0 additions & 17 deletions build-logic/settings.gradle.kts

This file was deleted.

15 changes: 0 additions & 15 deletions build-logic/src/main/kotlin/io/casper/build/TestClass.kt

This file was deleted.

7 changes: 5 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
kotlin("plugin.spring") version "1.9.23"
id("org.springframework.boot") version "3.4.4"
id("io.spring.dependency-management") version "1.1.7"
id("org.jlleitschuh.gradle.ktlint").version("12.1.1")
id("org.jlleitschuh.gradle.ktlint").version("11.5.1")
id("io.gitlab.arturbosch.detekt") version "1.23.6"
id("casper.documentation-convention")
}
Expand Down Expand Up @@ -40,7 +40,6 @@ group = "hs.kr.entrydsm"
version = "0.0.1-SNAPSHOT"

dependencies {

implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
Expand Down Expand Up @@ -76,3 +75,7 @@ tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {

jvmTarget = ("17") // Detekt가 사용하는 JVM 타겟을 Java 17로 지정
}

tasks.withType<org.springframework.boot.gradle.tasks.bundling.BootJar> {
mainClass.set("hs.kr.entrydsm.feed.CasperFeedApplication")
}
7 changes: 7 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
`kotlin-dsl`
}

repositories {
mavenCentral()
}
54 changes: 54 additions & 0 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
object Dependencies {
// Spring Boot
const val SPRING_BOOT_STARTER = "org.springframework.boot:spring-boot-starter"
const val SPRING_BOOT_STARTER_WEB = "org.springframework.boot:spring-boot-starter-web"
const val SPRING_BOOT_STARTER_DATA_JPA = "org.springframework.boot:spring-boot-starter-data-jpa"
const val SPRING_BOOT_STARTER_DATA_REDIS = "org.springframework.boot:spring-boot-starter-data-redis"
const val SPRING_BOOT_STARTER_SECURITY = "org.springframework.boot:spring-boot-starter-security"
const val SPRING_BOOT_STARTER_VALIDATION = "org.springframework.boot:spring-boot-starter-validation"
const val SPRING_BOOT_STARTER_TEST = "org.springframework.boot:spring-boot-starter-test"

// Kotlin
const val KOTLIN_REFLECT = "org.jetbrains.kotlin:kotlin-reflect"
const val KOTLIN_TEST_JUNIT5 = "org.jetbrains.kotlin:kotlin-test-junit5"

// Database
const val MYSQL_CONNECTOR = "com.mysql:mysql-connector-j"

// JSON
const val JACKSON_MODULE_KOTLIN = "com.fasterxml.jackson.module:jackson-module-kotlin"
const val ORG_JSON = "org.json:json:${DependencyVersion.ORG_JSON}"

// JWT
const val JWT_API = "io.jsonwebtoken:jjwt-api:${DependencyVersion.JWT}"
const val JWT_IMPL = "io.jsonwebtoken:jjwt-impl:${DependencyVersion.JWT}"
const val JWT_JACKSON = "io.jsonwebtoken:jjwt-jackson:${DependencyVersion.JWT}"

// MapStruct
const val MAPSTRUCT = "org.mapstruct:mapstruct:${DependencyVersion.MAPSTRUCT}"
const val MAPSTRUCT_PROCESSOR = "org.mapstruct:mapstruct-processor:${DependencyVersion.MAPSTRUCT}"

// Test
const val JUNIT_PLATFORM_LAUNCHER = "org.junit.platform:junit-platform-launcher"

// gRPC
const val GRPC_NETTY_SHADED = "io.grpc:grpc-netty-shaded:${DependencyVersion.GRPC}"
const val GRPC_PROTOBUF = "io.grpc:grpc-protobuf:${DependencyVersion.GRPC}"
const val GRPC_STUB = "io.grpc:grpc-stub:${DependencyVersion.GRPC}"
const val GRPC_KOTLIN_STUB = "io.grpc:grpc-kotlin-stub:${DependencyVersion.GRPC_KOTLIN}"
const val PROTOBUF_KOTLIN = "com.google.protobuf:protobuf-kotlin:${DependencyVersion.PROTOBUF}"
const val GRPC_TESTING = "io.grpc:grpc-testing:${DependencyVersion.GRPC}"


// swagger
const val SWAGGER = "org.springdoc:springdoc-openapi-starter-webmvc-ui:${DependencyVersion.SWAGGER_VERSION}"

// AWS
const val AWS = "com.amazonaws:aws-java-sdk-s3:${DependencyVersion.AWS}"

// open feign
const val OPEN_FEIGN = "org.springframework.cloud:spring-cloud-starter-openfeign:${DependencyVersion.OPEN_FEIGN_VERSION}"

// Kafka
const val KAFKA = "org.springframework.kafka:spring-kafka"
}
20 changes: 20 additions & 0 deletions buildSrc/src/main/kotlin/DependencyVersion.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
object DependencyVersion {
const val KOTLIN = "1.9.25"
const val SPRING_BOOT = "3.4.4"
const val SPRING_DEPENDENCY_MANAGEMENT = "1.1.7"
const val DETEKT = "1.23.6"
const val KTLINT = "12.1.1"

const val JWT = "0.11.5"
const val ORG_JSON = "20230227"
const val MAPSTRUCT = "1.6.0"

const val GRPC = "1.61.1"
const val GRPC_KOTLIN = "1.4.1"
const val PROTOBUF = "3.25.3"

const val SWAGGER_VERSION = "2.5.0"
const val AWS = "1.12.281"

const val OPEN_FEIGN_VERSION = "3.1.4"
}
11 changes: 11 additions & 0 deletions buildSrc/src/main/kotlin/Plugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
object Plugin {
const val KOTLIN_JVM = "org.jetbrains.kotlin.jvm"
const val KOTLIN_SPRING = "org.jetbrains.kotlin.plugin.spring"
const val KOTLIN_KAPT = "org.jetbrains.kotlin.kapt"
const val SPRING_BOOT = "org.springframework.boot"
const val SPRING_DEPENDENCY_MANAGEMENT = "io.spring.dependency-management"
const val DETEKT = "io.gitlab.arturbosch.detekt"
const val KTLINT = "org.jlleitschuh.gradle.ktlint"
const val CASPER_DOCUMENTATION = "casper.documentation-convention"
const val PROTOBUF = "com.google.protobuf"
}
8 changes: 8 additions & 0 deletions buildSrc/src/main/kotlin/PluginVersion.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object PluginVersion {
const val KOTLIN_VERSION = "1.9.25"
const val SPRING_BOOT_VERSION = "3.4.4"
const val SPRING_DEPENDENCY_MANAGEMENT_VERSION = "1.1.7"
const val DETEKT_VERSION = "1.23.6"
const val KTLINT_VERSION = "12.1.1"
const val PROTOBUF_VERSION = "0.9.4"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package hs.kr.entrydsm.feed

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.runApplication

/**
* CasperFeed 애플리케이션의 메인 클래스입니다.
*
* 이 클래스는 스프링 부트 애플리케이션을 시작하고 자동 구성을 활성화합니다.
* `@ConfigurationPropertiesScan` 어노테이션을 통해 설정 프로퍼티 클래스들을 스캔합니다.
*/
@SpringBootApplication
@ConfigurationPropertiesScan
class CasperFeedApplication

/**
* 애플리케이션의 진입점입니다.
*
* @param args 명령행 인자 배열
*/
fun main(args: Array<String>) {
runApplication<CasperFeedApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package hs.kr.entrydsm.feed.adapter.`in`.attachFile

import hs.kr.entrydsm.feed.application.attachFile.service.CreateAttachFileService
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile

/**
* 첨부 파일 관련 HTTP 요청을 처리하는 웹 어댑터 클래스입니다.
*
* 이 클래스는 첨부 파일 업로드와 관련된 HTTP 엔드포인트를 제공하며,
* 클라이언트의 요청을 적절한 서비스 메서드로 라우팅합니다.
*
* @property createAttachFileService 첨부 파일 비즈니스 로직을 처리하는 서비스
*/
@RestController
@RequestMapping("/attach-file")
class AttachFileWebAdapter(
private val createAttachFileUseCase: CreateAttachFileService,
) {
/**
* 하나 이상의 첨부 파일을 업로드하고, 업로드된 파일 정보를 반환합니다.
*
* 이 메서드는 클라이언트로부터 전송된 첨부 파일을 받아 서버에 저장하고,
* 저장된 파일에 대한 정보(파일명, 다운로드 URL 등)를 반환합니다.
*
* @param attachFile 업로드할 첨부 파일 목록 (multipart/form-data 형식의 'attach_file' 파라미터로 전달)
* @return 업로드된 첨부 파일 정보 목록 (CreateAttachFileResponse 리스트)
*
* @throws org.springframework.web.multipart.MultipartException 파일 업로드에 실패한 경우
* @throws java.io.IOException 파일 저장 중 I/O 오류가 발생한 경우
* @throws hs.kr.entrydsm.feed.global.error.exception.InternalServerErrorException 내부 서버 오류가 발생한 경우
*
* @see CreateAttachFileService.execute
*/
@PostMapping
fun createAttachFile(
@RequestPart(value = "attach_file") attachFile: List<MultipartFile>,
) = createAttachFileUseCase.execute(attachFile)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package hs.kr.entrydsm.feed.adapter.`in`.attachFile.dto.response

/**
* 첨부 파일 생성 응답을 위한 데이터 클래스입니다.
*
* @property fileName 원본 파일 이름
* @property url 업로드된 파일에 접근할 수 있는 URL
*/
data class CreateAttachFileResponse(
val fileName: String,
val url: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package hs.kr.entrydsm.feed.adapter.`in`.notice

import hs.kr.entrydsm.feed.adapter.`in`.notice.dto.request.CreateNoticeRequest
import hs.kr.entrydsm.feed.adapter.`in`.notice.dto.request.UpdateNoticeRequest
import hs.kr.entrydsm.feed.adapter.`in`.notice.dto.response.QueryDetailsNoticeResponse
import hs.kr.entrydsm.feed.adapter.`in`.notice.dto.response.QueryListNoticeResponse
import hs.kr.entrydsm.feed.adapter.`in`.notice.dto.response.QueryNoticeTitleResponse
import hs.kr.entrydsm.feed.adapter.`in`.notice.dto.response.UploadNoticeImageResponse
import hs.kr.entrydsm.feed.application.notice.port.`in`.CreateNoticeUseCase
import hs.kr.entrydsm.feed.application.notice.port.`in`.DeleteNoticeUseCase
import hs.kr.entrydsm.feed.application.notice.port.`in`.QueryDetailsNoticeUseCase
import hs.kr.entrydsm.feed.application.notice.port.`in`.QueryNoticeListByTypeUseCase
import hs.kr.entrydsm.feed.application.notice.port.`in`.QueryNoticeTitleUseCase
import hs.kr.entrydsm.feed.application.notice.port.`in`.UpdateNoticeUseCase
import hs.kr.entrydsm.feed.application.notice.port.`in`.UploadNoticeImageUseCase
import hs.kr.entrydsm.feed.model.notice.type.NoticeType
import jakarta.validation.Valid
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import java.util.UUID

/**
* 공지사항 관련 HTTP 요청을 처리하는 웹 어댑터 클래스입니다.
*
* 이 클래스는 공지사항과 관련된 모든 HTTP 엔드포인트를 제공하며,
* 클라이언트의 요청을 적절한 서비스 메서드로 라우팅합니다.
*
* @property noticeService 공지사항 비즈니스 로직을 처리하는 서비스
*/
@RestController
@RequestMapping("/notice")
class NoticeWebAdapter(
private val createNoticeUseCase: CreateNoticeUseCase,
private val updateNoticeUseCase: UpdateNoticeUseCase,
private val deleteNoticeUseCase: DeleteNoticeUseCase,
private val queryDetailsNoticeUseCase: QueryDetailsNoticeUseCase,
private val queryNoticeTitleUseCase: QueryNoticeTitleUseCase,
private val uploadNoticeImageUseCase: UploadNoticeImageUseCase,
private val queryListNoticeListByTypeUseCase: QueryNoticeListByTypeUseCase,
) {
/**
* 새로운 공지사항을 생성합니다.
*
* @param createNoticeRequest 공지사항 생성 요청 데이터
*/
@ResponseStatus(value = HttpStatus.CREATED)
@PostMapping
fun createNotice(
@RequestBody @Valid
createNoticeRequest: CreateNoticeRequest,
) {
createNoticeUseCase.execute(createNoticeRequest)
}

/**
* 기존 공지사항을 수정합니다.
*
* @param id 수정할 공지사항의 고유 식별자
* @param updateNoticeRequest 공지사항 수정 요청 데이터
* @return 수정 결과에 대한 응답 엔티티
*/
@PatchMapping("/{notice-id}")
fun updateNotice(
@PathVariable(name = "notice-id") id: UUID,
@RequestBody updateNoticeRequest: UpdateNoticeRequest,
): ResponseEntity<String> = updateNoticeUseCase.execute(id, updateNoticeRequest)

/**
* 공지사항에 첨부할 이미지를 업로드합니다.
*
* @param image 업로드할 이미지 파일
* @return 업로드된 이미지 정보가 포함된 응답 객체
*/
@PostMapping("/image")
fun uploadImage(
@RequestPart(name = "photo") image: MultipartFile,
): UploadNoticeImageResponse = uploadNoticeImageUseCase.execute(image)

/**
* 모든 공지사항의 제목 목록을 조회합니다.
*
* @return 공지사항 제목 목록이 포함된 응답 객체 리스트
*/
@GetMapping("/title")
fun queryNoticeTitle(): List<QueryNoticeTitleResponse> = queryNoticeTitleUseCase.execute()

/**
* 특정 공지사항의 상세 정보를 조회합니다.
*
* @param noticeId 조회할 공지사항의 고유 식별자
* @return 공지사항 상세 정보가 포함된 응답 객체
*/
@GetMapping("/{notice-id}")
fun queryDetailsNotice(
@PathVariable(name = "notice-id", required = true)
noticeId: UUID,
): QueryDetailsNoticeResponse = queryDetailsNoticeUseCase.execute(noticeId)

/**
* 특정 유형의 공지사항 목록을 조회합니다.
*
* @param noticeType 조회할 공지사항 유형 (선택 사항)
* @return 해당 유형의 공지사항 목록이 포함된 응답 객체
*/
@GetMapping
fun queryNoticeListByType(
@RequestParam("type") noticeType: NoticeType?,
): QueryListNoticeResponse = queryListNoticeListByTypeUseCase.execute(noticeType)

/**
* 특정 공지사항을 삭제합니다.
*
* @param id 삭제할 공지사항의 고유 식별자
*/
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping("/{notice-id}")
fun deleteNotice(
@PathVariable(name = "notice-id")id: UUID,
) = deleteNoticeUseCase.execute(id)
}
Loading