Skip to content

Commit a7946a3

Browse files
authored
Merge branch 'epic/board' into file-upload
2 parents c0f607d + 89c8394 commit a7946a3

File tree

62 files changed

+1889
-265
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1889
-265
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
## 기능 설명
2-
3-
## 작업 내용
4-
2+
-
3+
### 작업 내용
4+
-
55
## 수정 사항
6-
6+
-
77
## 추가 작업 예정
8+
-
9+
### 테스트
10+
- [ ] 단위 테스트 확인
11+
- [ ] 빌드 테스트 확인
12+
- [ ] 비정상 입력 시 오류 메시지 확인
13+
- [ ] AWS에 서버 올라가는지 / Swagger 확인

.github/workflows/deploy.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ on:
1111
push:
1212
branches:
1313
- dev
14-
pull_request:
15-
branches:
16-
- dev
1714

1815
jobs:
1916
build-and-push:

.github/workflows/pr-merged.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Notify Slack on PR Closed for Dev Branch
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- opened
7+
- reopened
8+
9+
jobs:
10+
notify-slack:
11+
if: github.event.pull_request.base.ref == 'dev'
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Debug PR URL and Body
15+
run: |
16+
echo "PR URL: ${{ github.event.pull_request.html_url }}"
17+
echo "PR Body: ${{ github.event.pull_request.body }}"
18+
19+
- name: Send notification to Slack
20+
uses: slackapi/[email protected]
21+
with:
22+
payload: |
23+
{
24+
"text": "🚀 Pull Request *#${{ github.event.pull_request.number }}* has been merged into *dev* branch!",
25+
"attachments": [
26+
{
27+
"title": "PR Details",
28+
"title_link": "${{ github.event.pull_request.html_url }}",
29+
"text": ${{ toJson(github.event.pull_request.body) }}
30+
}
31+
]
32+
}
33+
env:
34+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ dependencies {
2929
implementation 'org.springframework.boot:spring-boot-starter-web'
3030
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
3131

32+
// CHAT
33+
// web socket
34+
implementation 'org.springframework.boot:spring-boot-starter-websocket'
35+
implementation 'org.webjars:webjars-locator-core'
36+
implementation 'org.webjars:sockjs-client:1.5.1'
37+
implementation 'org.webjars:stomp-websocket:2.3.4'
38+
// kafka
39+
implementation 'org.springframework.kafka:spring-kafka'
40+
3241
compileOnly 'org.projectlombok:lombok'
3342

3443
developmentOnly 'org.springframework.boot:spring-boot-devtools'

docker-compose.yml

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,41 @@ services:
2424
DOCKER_USERNAME: ${DOCKER_USERNAME}
2525
DOCKER_PASSWORD: ${DOCKER_PASSWORD}
2626
DB_ENDPOINT: ${DB_ENDPOINT}
27+
FRONT_URL: ${FRONT_URL}
2728
DB_NAME: ${DB_NAME}
2829
DB_PASSWORD: ${DB_PASSWORD}
29-
restart: always # 컨테이너가 중단되었을 때 재시작 설정
30+
SLACK_WEBHOOK_URL: ${SLACK_WEBHOOK_URL}
31+
depends_on:
32+
- kafka
33+
restart: on-failure
3034

31-
minio:
35+
zookeeper:
36+
image: confluentinc/cp-zookeeper:latest
37+
container_name: zookeeper
38+
environment:
39+
ZOOKEEPER_CLIENT_PORT: 2181
40+
ports:
41+
- "2181:2181"
42+
restart: always
43+
44+
kafka:
45+
image: confluentinc/cp-kafka:latest
46+
container_name: kafka
47+
ports:
48+
- "9092:9092"
49+
environment:
50+
KAFKA_BROKER_ID: 1
51+
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
52+
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
53+
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
54+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT
55+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
56+
KAFKA_HEAP_OPTS: "-Xms256M -Xmx512M" # JVM 힙 메모리 최소/최대 설정
57+
depends_on:
58+
- zookeeper
59+
restart: always
60+
61+
minio:
3262
image: minio/minio
3363
container_name: minio
3464
environment:
@@ -42,4 +72,4 @@ services:
4272

4373
volumes:
4474
minio_data:
45-
driver: local
75+
driver: local
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.myteam.server.auth.controller;
2+
3+
import jakarta.servlet.http.HttpServletResponse;
4+
import jakarta.validation.Valid;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.myteam.server.auth.dto.AuthRequest;
8+
import org.myteam.server.auth.dto.AuthResponse;
9+
import org.myteam.server.global.security.jwt.JwtProvider;
10+
import org.myteam.server.global.web.response.ResponseDto;
11+
import org.myteam.server.member.controller.response.MemberResponse;
12+
import org.myteam.server.member.dto.MemberSaveRequest;
13+
import org.myteam.server.member.entity.Member;
14+
import org.myteam.server.member.repository.MemberJpaRepository;
15+
import org.myteam.server.member.service.MemberService;
16+
import org.springframework.http.HttpStatus;
17+
import org.springframework.http.ResponseEntity;
18+
import org.springframework.security.authentication.AuthenticationManager;
19+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
20+
import org.springframework.security.core.Authentication;
21+
import org.springframework.security.core.userdetails.UsernameNotFoundException;
22+
import org.springframework.validation.BindingResult;
23+
import org.springframework.web.bind.annotation.PostMapping;
24+
import org.springframework.web.bind.annotation.RequestBody;
25+
import org.springframework.web.bind.annotation.RequestMapping;
26+
import org.springframework.web.bind.annotation.RestController;
27+
28+
import java.time.Duration;
29+
30+
import static org.myteam.server.global.security.jwt.JwtProvider.*;
31+
import static org.myteam.server.global.web.response.ResponseStatus.SUCCESS;
32+
33+
@Slf4j
34+
@RestController
35+
@RequestMapping("/api/test")
36+
@RequiredArgsConstructor
37+
public class AuthTestController {
38+
39+
private final AuthenticationManager authenticationManager;
40+
private final JwtProvider jwtProvider;
41+
private final MemberJpaRepository memberRepository;
42+
private final MemberService memberService;
43+
44+
@PostMapping("/create")
45+
public ResponseEntity<?> create(@RequestBody @Valid MemberSaveRequest memberSaveRequest,
46+
BindingResult bindingResult,
47+
HttpServletResponse httpServletResponse
48+
) {
49+
log.info("MyInfoController create 메서드 실행");
50+
MemberResponse response = memberService.create(memberSaveRequest);
51+
52+
// Authorization
53+
String accessToken = jwtProvider.generateToken(TOKEN_CATEGORY_ACCESS, Duration.ofDays(1), response.getPublicId(), response.getRole().name(), response.getStatus().name());
54+
55+
// 응답 헤더 설정
56+
httpServletResponse.addHeader(HEADER_AUTHORIZATION, TOKEN_PREFIX + accessToken);
57+
return new ResponseEntity<>(new ResponseDto<>(SUCCESS.name(), "회원가입 성공", response), HttpStatus.CREATED);
58+
}
59+
60+
@PostMapping("/login")
61+
public ResponseEntity<AuthResponse> login(@RequestBody AuthRequest request) {
62+
log.info("🔐 로그인 요청 - email: {}", request.getEmail());
63+
64+
// 1. 사용자 인증 시도
65+
Authentication authentication = authenticationManager.authenticate(
66+
new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())
67+
);
68+
69+
// 2. 사용자 정보 조회
70+
Member member = memberRepository.findByEmail(request.getEmail())
71+
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다."));
72+
73+
// 3. JWT 토큰 생성
74+
String accessToken1d = jwtProvider.generateToken(TOKEN_CATEGORY_ACCESS, Duration.ofDays(1), member.getPublicId(), member.getRole().name(), member.getStatus().name());
75+
String accessToken30s = jwtProvider.generateToken(TOKEN_CATEGORY_ACCESS, Duration.ofSeconds(30), member.getPublicId(), member.getRole().name(), member.getStatus().name());
76+
77+
log.info("✅ 로그인 성공 - email: {}, accessToken1d: {}, accessToken30s: {}", member.getEmail(), accessToken1d, accessToken30s);
78+
79+
// 4. 응답 반환
80+
return ResponseEntity.ok(new AuthResponse(accessToken1d, accessToken30s));
81+
}
82+
}

src/main/java/org/myteam/server/auth/controller/ReIssueController.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@
1111
import org.springframework.web.bind.annotation.PostMapping;
1212
import org.springframework.web.bind.annotation.RestController;
1313

14-
import java.net.URLEncoder;
15-
import java.nio.charset.StandardCharsets;
16-
1714
import static org.myteam.server.global.exception.ErrorCode.INTERNAL_SERVER_ERROR;
1815
import static org.myteam.server.global.security.jwt.JwtProvider.*;
1916
import static org.myteam.server.global.util.cookie.CookieUtil.createCookie;
17+
import static org.myteam.server.global.util.domain.DomainUtil.extractDomain;
2018

2119
/**
2220
* TODO_ : 리프레시 토큰에 대한 블랙 리스트 작성
@@ -47,18 +45,20 @@ public ResponseEntity<?> reissue(HttpServletRequest request, HttpServletResponse
4745
// Refresh Token 쿠키 추가
4846
response.addCookie(createCookie(
4947
REFRESH_TOKEN_KEY,
50-
URLEncoder.encode(TOKEN_PREFIX + tokens.getRefreshToken(), StandardCharsets.UTF_8),
48+
tokens.getRefreshToken(),
5149
TOKEN_REISSUE_PATH,
5250
24 * 60 * 60,
53-
true
51+
true,
52+
extractDomain(request.getServerName())
5453
));
5554

5655
response.addCookie(createCookie(
5756
REFRESH_TOKEN_KEY,
58-
URLEncoder.encode(TOKEN_PREFIX + tokens.getRefreshToken(), StandardCharsets.UTF_8),
57+
tokens.getRefreshToken(),
5958
LOGOUT_PATH,
6059
24 * 60 * 60,
61-
true
60+
true,
61+
extractDomain(request.getServerName())
6262
));
6363
return new ResponseEntity<>(HttpStatus.OK);
6464
} catch (PlayHiveException e) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.myteam.server.auth.dto;
2+
3+
import lombok.Getter;
4+
import lombok.NoArgsConstructor;
5+
6+
@Getter
7+
@NoArgsConstructor
8+
public class AuthRequest {
9+
private String email;
10+
private String password;
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.myteam.server.auth.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
public class AuthResponse {
9+
private String accessToken1d;
10+
private String accessToken30s;
11+
}

src/main/java/org/myteam/server/auth/repository/RefreshJpaRepository.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
import org.springframework.data.jpa.repository.JpaRepository;
55
import org.springframework.transaction.annotation.Transactional;
66

7-
import java.util.Optional;
87
import java.util.UUID;
98

109
public interface RefreshJpaRepository extends JpaRepository<Refresh, Long> {
1110
Boolean existsByRefreshAndPublicId(String refresh, UUID publicId);
12-
Optional<Refresh> findByPublicId(UUID publicId); // 테스트 용으로 추가함
11+
1312
@Transactional
14-
void deleteByRefreshAndPublicId(String refresh, UUID publicId);
13+
void deleteByPublicId(UUID publicId);
1514
}

0 commit comments

Comments
 (0)