Skip to content

[조현아] sprint7 #202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: 조현아
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@

## 요구사항

### 기본

- [x] 기본 항목 1
- [ ] 기본 항목 2

### 심화

- [ ] 심화 항목 1
- [ ] 심화 항목 2

## 주요 변경사항
-
-

-

## 스크린샷

![image](이미지url)

## 멘토에게

- 셀프 코드 리뷰를 통해 질문 이어가겠습니다.
-
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ out/
### VS Code ###
.vscode/

# 파일 저장소 (사용자가 업로드한 파일들)
.discodeit/storage/
/binary-contents/
*.ser

# 로그 파일들
logs/
.logs/
*.log

# 기타
*.ser
3 changes: 3 additions & 0 deletions HELP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Getting Started

### Reference Documentation

For further reference, please consider the following sections:

* [Official Gradle documentation](https://docs.gradle.org)
Expand All @@ -9,13 +10,15 @@ For further reference, please consider the following sections:
* [Spring Web](https://docs.spring.io/spring-boot/3.4.4/reference/web/servlet.html)

### Guides

The following guides illustrate how to use some features concretely:

* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)

### Additional Links

These additional references should also help you:

* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle)
Expand Down
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# 🛠️ Discodeit

---
Spring Boot 기반의 메시징 시스템 프로젝트

## 📌 프로젝트 개요

Discodeit은 채널 기반의 커뮤니케이션 서비스를 제공하는 백엔드 서버입니다.
Public / Private 채널 생성, 메시지 전송, 사용자 상태 추적 등의 기능을 지원합니다.

---

## ⚙️ 기술 스택

- **Language**: Java 17
- **Framework**: Spring Boot 3.4.4
- **Database**: H2 (dev), PostgreSQL (prod)
- **ORM**: Spring Data JPA (Hibernate)
- **API Docs**: Springdoc OpenAPI (Swagger UI)
- **빌드 도구**: Gradle
- **로깅**: Logback, SLF4J + AOP 기반 로깅

---

## 🧩 프로파일 기반 설정

- `application-dev.yaml`: H2 DB, 서버 포트 8080
- `application-prod.yaml`: PostgreSQL, 서버 포트 8080

모든 프로파일은 공통 설정을 `application.yaml`에서 상속합니다.

---

## 🚀 실행 방법

### 1. 개발 환경 (H2 사용)

### 2. 운영 환경 (PostgreSql 사용)

---

## 🔗 주요 API 엔드포인트

| 기능 | Method | Endpoint | 설명 |
|--------|--------|--------------------------------|------------------------|
| 사용자 생성 | POST | `/api/users` | 사용자 등록 |
| 채널 생성 | Get | `/api/channels` | PUBLIC / PRIVATE 채널 생성 |
| 메시지 전송 | POST | `/api/messages` | 채널에 메시지 전송 |
| 메시지 목록 | GET | `/api/messages?channelId={id}` | 특정 채널의 메시지 조회 ||

---

## 🧾 패키지 구조

```
com.sprint.mission.discodeit
├── aop # 공통 로깅/트랜잭션을 위한 AOP 설정
├── common # 상수, 유틸리티, 공용 포맷 등
├── config # Swagger, WebMvc, 로깅 등 설정 클래스
├── controller # API 컨트롤러 계층
│ └── api
├── dto # 요청/응답용 DTO 계층
│ ├── data # 내부 응답 또는 공용 데이터 구조
│ ├── request # 클라이언트 요청 DTO
│ └── response # API 응답 DTO
├── entity # JPA 엔티티 클래스
│ └── base
├── exception # 커스텀 예외 및 예외 계층 구조
├── mapper # DTO ↔ Entity 변환 전용 매퍼
├── repository # JPA Repository 인터페이스 정의
├── service # 비즈니스 로직 계층
│ ├── basic
├── storage
│ └── local # 파일 저장소 구현체 (로컬)
└── DiscodeitApplication # 스프링 부트 메인 애플리케이션
```

---

## 🧵 추가 구성

- `logback-spring.xml` 설정 완료
- `.logs` 디렉토리에 일자별 파일 로그 저장
- 콘솔 + 파일 동시 출력
14 changes: 8 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,22 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'org.postgresql:postgresql'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.mapstruct:mapstruct:1.6.3'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
runtimeOnly 'org.postgresql:postgresql'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// mapstruct
implementation 'org.mapstruct:mapstruct:1.6.3'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
runtimeOnly 'com.h2database:h2'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class DiscodeitApplication {

public static void main(String[] args) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/sprint/mission/discodeit/aop/AopConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sprint.mission.discodeit.aop;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

}
10 changes: 10 additions & 0 deletions src/main/java/com/sprint/mission/discodeit/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sprint.mission.discodeit.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class AppConfig {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.sprint.mission.discodeit.config;

import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class LoggingAspect {

/**
* 컨트롤러 계층의 하위 패키지 포함 모든 메서드에 대한 포인트 컷
*/
@Pointcut("execution(* com.sprint.mission.discodeit.controller..*.*(..))")
private void controllerLayer() {

}

/**
* 서비스 계층의 하위 패키지 포함 모든 메서드에 대한 포인트 컷
*/
@Pointcut("execution(* com.sprint.mission.discodeit.service..*.*(..))")
private void serviceLayer() {

}

@Around("controllerLayer()")
public Object logController(ProceedingJoinPoint joinPoint) throws Throwable {
return logExecution(joinPoint, "Controller");
}

@Around("serviceLayer()")
public Object logService(ProceedingJoinPoint joinPoint) throws Throwable {
return logExecution(joinPoint, "Service");
}

/**
* 공통 로깅 로직 수행하는 메서드 메서드 실행 전후로 로그 출력
*
* @param joinPoint 실행 대상 메서드에 대한 정보
* @param layer "Controller", "Service" 구분용 문자열
* @return 실행 결과 (원래 메서드의 반환값)
* @throws Throwable 메서드 실행 중 발생할 수 있는 예외
*/
private Object logExecution(ProceedingJoinPoint joinPoint, String layer) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String className = signature.getDeclaringType().getSimpleName();
String methodName = signature.getName();

log.info("[{}] {}#{} 시작 - args={}", layer, className, methodName,
Arrays.toString(joinPoint.getArgs()));

long startTime = System.currentTimeMillis();

try {
Object result = joinPoint.proceed();
long elapsed = System.currentTimeMillis() - startTime;

log.info("[{}] {}#{} 완료 - {}ms - result={}", layer, className, methodName, elapsed,
result != null ? result.getClass().getSimpleName() : null);

return result;
} catch (Throwable t) {
long elapsed = System.currentTimeMillis() - startTime;
log.error("[{}] {}#{} 예외 발생 - {}ms - {}: {}", layer, className, methodName, elapsed,
t.getClass().getSimpleName(), t.getMessage());

throw t;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.servers.Server;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class SwaggerConfig {

Expand All @@ -18,7 +17,7 @@ public OpenAPI openAPI() {
.info(new Info()
.title("Discodeit API")
.description("Discodeit 프로젝트의 Swagger API 문서입니다.")
.version("v1.0.0")
.version("v1.2")
.contact(new Contact()
.name("조현아")
.email("[email protected]")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,52 @@
package com.sprint.mission.discodeit.controller;

import com.sprint.mission.discodeit.controller.api.AuthApi;
import com.sprint.mission.discodeit.dto.data.UserDto;
import com.sprint.mission.discodeit.dto.request.LoginRequest;
import com.sprint.mission.discodeit.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
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 io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/auth")
public class AuthController implements AuthApi {
@Tag(name = "Auth", description = "인증 API")
public class AuthController {

private final AuthService authService;

@PostMapping(path = "/login")
public ResponseEntity<UserDto> login(@RequestBody LoginRequest loginRequest) {
@Operation(summary = "로그인")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200", description = "로그인 성공",
content = @Content(schema = @Schema(implementation = UserDto.class))
),
@ApiResponse(
responseCode = "400", description = "비밀번호가 일치하지 않음",
content = @Content(examples = @ExampleObject(value = "Wrong password"))
),
@ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음",
content = @Content(examples = @ExampleObject(value = "User with username {username} not found"))
)
})
public ResponseEntity<UserDto> login(
@Parameter(description = "로그인 정보") @RequestBody @Valid LoginRequest loginRequest) {
UserDto user = authService.login(loginRequest);

return ResponseEntity
Expand Down
Loading