Skip to content

Commit 7455fa1

Browse files
authored
Merge branch 'dev' into Fix/264
2 parents ef9ff43 + 35e9689 commit 7455fa1

File tree

19 files changed

+2099
-52
lines changed

19 files changed

+2099
-52
lines changed

infra/terraform/main.tf

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,51 @@ resource "aws_security_group" "sg_1" {
125125
Name = "team5-sg-1"
126126
}
127127

128+
# SSH
128129
ingress {
129-
from_port = 0
130-
to_port = 0
131-
protocol = "all" # 모든 프로토콜
132-
cidr_blocks = ["0.0.0.0/0"] # 모든 IP 허용
130+
from_port = 22
131+
to_port = 22
132+
protocol = "tcp"
133+
cidr_blocks = ["0.0.0.0/0"] # 필요 시 특정 IP로 제한 가능
134+
}
135+
136+
# HTTP
137+
ingress {
138+
from_port = 80
139+
to_port = 80
140+
protocol = "tcp"
141+
cidr_blocks = ["0.0.0.0/0"]
142+
}
143+
144+
# HTTPS
145+
ingress {
146+
from_port = 443
147+
to_port = 443
148+
protocol = "tcp"
149+
cidr_blocks = ["0.0.0.0/0"]
150+
}
151+
152+
# WebRTC UDP
153+
ingress {
154+
from_port = 10000
155+
to_port = 20000
156+
protocol = "udp"
157+
cidr_blocks = ["0.0.0.0/0"]
133158
}
134159

160+
# NPM (port 81)
161+
ingress {
162+
from_port = 81
163+
to_port = 81
164+
protocol = "tcp"
165+
cidr_blocks = ["0.0.0.0/0"]
166+
}
167+
168+
# 아웃바운드 모든 프로토콜
135169
egress {
136170
from_port = 0
137171
to_port = 0
138-
protocol = "all" # 모든 프로토콜
172+
protocol = "all"
139173
cidr_blocks = ["0.0.0.0/0"] # 모든 IP 허용
140174
}
141175
}

src/main/java/com/back/domain/board/post/entity/Post.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ public void addPostCategoryMapping(PostCategoryMapping mapping) {
6262
this.postCategoryMappings.add(mapping);
6363
}
6464

65+
public void removePostCategoryMapping(PostCategoryMapping mapping) {
66+
this.postCategoryMappings.remove(mapping);
67+
}
68+
6569
public void addLike(PostLike like) {
6670
this.postLikes.add(like);
6771
}
@@ -93,11 +97,23 @@ public void update(String title, String content) {
9397
this.content = content;
9498
}
9599

96-
// TODO: 진짜로 바뀐 카테고리만 추가/삭제하도록 개선
97100
/** 카테고리 일괄 업데이트 */
98-
public void updateCategories(List<PostCategory> categories) {
99-
this.postCategoryMappings.clear();
100-
categories.forEach(category -> new PostCategoryMapping(this, category));
101+
public void updateCategories(List<PostCategory> newCategories) {
102+
List<PostCategory> currentCategories = this.getCategories();
103+
104+
// 제거 대상
105+
List<PostCategoryMapping> toRemove = this.getPostCategoryMappings().stream()
106+
.filter(mapping -> !newCategories.contains(mapping.getCategory()))
107+
.toList();
108+
109+
// 추가 대상
110+
List<PostCategory> toAdd = newCategories.stream()
111+
.filter(category -> !currentCategories.contains(category))
112+
.toList();
113+
114+
// 실행
115+
toRemove.forEach(this::removePostCategoryMapping);
116+
toAdd.forEach(category -> new PostCategoryMapping(this, category));
101117
}
102118

103119
/** 좋아요 수 증가 */

src/main/java/com/back/domain/board/post/service/PostService.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,18 @@ public PostResponse createPost(PostRequest request, Long userId) {
4949
// Post 생성
5050
Post post = new Post(user, request.title(), request.content(), request.thumbnailUrl());
5151

52+
// Post 저장
53+
Post saved = postRepository.save(post);
54+
5255
// Category 매핑
5356
if (request.categoryIds() != null) {
5457
List<PostCategory> categories = postCategoryRepository.findAllById(request.categoryIds());
5558
if (categories.size() != request.categoryIds().size()) {
5659
throw new CustomException(ErrorCode.CATEGORY_NOT_FOUND);
5760
}
58-
post.updateCategories(categories);
61+
saved.updateCategories(categories);
5962
}
6063

61-
// Post 저장 및 응답 반환
62-
Post saved = postRepository.save(post);
6364
return PostResponse.from(saved);
6465
}
6566

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package com.back.domain.file.entity;
22

33
public enum EntityType {
4-
POST, COMMENT
4+
POST, AVATAR, PROFILE
55
}

src/main/java/com/back/domain/file/service/FileService.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,8 @@
77
import com.back.domain.file.dto.FileReadResponseDto;
88
import com.back.domain.file.dto.FileUpdateResponseDto;
99
import com.back.domain.file.dto.FileUploadResponseDto;
10-
import com.back.domain.file.entity.AttachmentMapping;
11-
import com.back.domain.file.entity.EntityType;
1210
import com.back.domain.file.entity.FileAttachment;
13-
import com.back.domain.file.repository.AttachmentMappingRepository;
1411
import com.back.domain.file.repository.FileAttachmentRepository;
15-
import com.back.domain.file.util.EntityValidator;
1612
import com.back.domain.user.entity.User;
1713
import com.back.domain.user.repository.UserRepository;
1814
import com.back.global.exception.CustomException;
@@ -21,8 +17,6 @@
2117
import org.springframework.beans.factory.annotation.Value;
2218
import org.springframework.stereotype.Service;
2319
import org.springframework.transaction.annotation.Transactional;
24-
import org.springframework.web.bind.annotation.PathVariable;
25-
import org.springframework.web.bind.annotation.RequestParam;
2620
import org.springframework.web.multipart.MultipartFile;
2721

2822
import java.io.IOException;

src/main/java/com/back/domain/file/util/EntityValidator.java

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.back.domain.studyroom.controller;
2+
3+
import com.back.domain.studyroom.dto.InviteCodeResponse;
4+
import com.back.domain.studyroom.entity.RoomInviteCode;
5+
import com.back.domain.studyroom.service.RoomInviteService;
6+
import com.back.global.common.dto.RsData;
7+
import com.back.global.security.user.CurrentUser;
8+
import io.swagger.v3.oas.annotations.Operation;
9+
import io.swagger.v3.oas.annotations.Parameter;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
13+
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.http.HttpStatus;
16+
import org.springframework.http.ResponseEntity;
17+
import org.springframework.web.bind.annotation.*;
18+
19+
/**
20+
* 방 초대 코드 API 컨트롤러
21+
* - VISITOR 포함 모든 사용자가 초대 코드 발급 가능
22+
* - 사용자당 1개의 고유 코드 보유
23+
* - 3시간 유효, 만료 후 재생성 가능
24+
*/
25+
@RestController
26+
@RequestMapping("/api/rooms/{roomId}/invite")
27+
@RequiredArgsConstructor
28+
@Tag(name = "Room Invite API", description = "방 초대 코드 관련 API")
29+
@SecurityRequirement(name = "Bearer Authentication")
30+
public class RoomInviteController {
31+
32+
private final RoomInviteService inviteService;
33+
private final CurrentUser currentUser;
34+
35+
@GetMapping("/me")
36+
@Operation(
37+
summary = "내 초대 코드 조회/생성",
38+
description = "내 초대 코드를 조회합니다. 없으면 자동으로 생성됩니다. " +
39+
"유효기간은 3시간이며, 만료 전까지는 같은 코드가 유지됩니다. " +
40+
"만료된 경우 새로 생성할 수 있습니다."
41+
)
42+
@ApiResponses({
43+
@ApiResponse(responseCode = "200", description = "조회/생성 성공"),
44+
@ApiResponse(responseCode = "404", description = "존재하지 않는 방"),
45+
@ApiResponse(responseCode = "401", description = "인증 실패")
46+
})
47+
public ResponseEntity<RsData<InviteCodeResponse>> getMyInviteCode(
48+
@Parameter(description = "방 ID", required = true) @PathVariable Long roomId) {
49+
50+
Long userId = currentUser.getUserId();
51+
52+
RoomInviteCode code = inviteService.getOrCreateMyInviteCode(roomId, userId);
53+
InviteCodeResponse response = InviteCodeResponse.from(code);
54+
55+
return ResponseEntity
56+
.status(HttpStatus.OK)
57+
.body(RsData.success("초대 코드 조회 완료", response));
58+
}
59+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.back.domain.studyroom.controller;
2+
3+
import com.back.domain.studyroom.dto.JoinRoomResponse;
4+
import com.back.domain.studyroom.entity.Room;
5+
import com.back.domain.studyroom.entity.RoomMember;
6+
import com.back.domain.studyroom.service.RoomInviteService;
7+
import com.back.domain.studyroom.service.RoomService;
8+
import com.back.global.common.dto.RsData;
9+
import com.back.global.security.user.CurrentUser;
10+
import io.swagger.v3.oas.annotations.Operation;
11+
import io.swagger.v3.oas.annotations.Parameter;
12+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
13+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
14+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
15+
import io.swagger.v3.oas.annotations.tags.Tag;
16+
import lombok.RequiredArgsConstructor;
17+
import org.springframework.http.HttpStatus;
18+
import org.springframework.http.ResponseEntity;
19+
import org.springframework.web.bind.annotation.*;
20+
21+
/**
22+
* 초대 코드 입장 API
23+
* - 비로그인 시 401 반환 (프론트에서 로그인 페이지로 리다이렉트)
24+
*/
25+
@RestController
26+
@RequestMapping("/api/invite")
27+
@RequiredArgsConstructor
28+
@Tag(name = "Room Invite API", description = "초대 코드로 방 입장 API")
29+
public class RoomInvitePublicController {
30+
31+
private final RoomInviteService inviteService;
32+
private final RoomService roomService;
33+
private final CurrentUser currentUser;
34+
35+
@PostMapping("/{inviteCode}")
36+
@SecurityRequirement(name = "Bearer Authentication")
37+
@Operation(
38+
summary = "초대 코드로 방 입장",
39+
description = "초대 코드를 사용하여 방에 입장합니다. " +
40+
"비밀번호가 걸린 방도 초대 코드로 입장 가능합니다. " +
41+
"비로그인 사용자는 401 응답을 받습니다 (프론트에서 로그인 페이지로 이동)."
42+
)
43+
@ApiResponses({
44+
@ApiResponse(responseCode = "200", description = "입장 성공"),
45+
@ApiResponse(responseCode = "400", description = "만료되었거나 유효하지 않은 코드"),
46+
@ApiResponse(responseCode = "404", description = "존재하지 않는 초대 코드"),
47+
@ApiResponse(responseCode = "401", description = "인증 필요 (비로그인)")
48+
})
49+
public ResponseEntity<RsData<JoinRoomResponse>> joinByInviteCode(
50+
@Parameter(description = "초대 코드", required = true, example = "A3B9C2D1")
51+
@PathVariable String inviteCode) {
52+
53+
// 로그인 체크는 Spring Security에서 자동 처리 (비로그인 시 401)
54+
Long userId = currentUser.getUserId();
55+
56+
// 초대 코드 검증 및 방 조회
57+
Room room = inviteService.getRoomByInviteCode(inviteCode);
58+
59+
// 방 입장 (비밀번호 무시)
60+
RoomMember member = roomService.joinRoom(room.getId(), null, userId);
61+
JoinRoomResponse response = JoinRoomResponse.from(member);
62+
63+
return ResponseEntity
64+
.status(HttpStatus.OK)
65+
.body(RsData.success("초대 코드로 입장 완료", response));
66+
}
67+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.back.domain.studyroom.dto;
2+
3+
import com.back.domain.studyroom.entity.RoomInviteCode;
4+
import com.fasterxml.jackson.annotation.JsonFormat;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
10+
import java.time.LocalDateTime;
11+
12+
@Getter
13+
@Builder
14+
@AllArgsConstructor
15+
@Schema(description = "초대 코드 응답")
16+
public class InviteCodeResponse {
17+
18+
@Schema(description = "초대 코드", example = "A3B9C2D1")
19+
private String inviteCode;
20+
21+
@Schema(description = "초대 링크", example = "https://catfe.com/invite/A3B9C2D1")
22+
private String inviteLink;
23+
24+
@Schema(description = "방 ID", example = "1")
25+
private Long roomId;
26+
27+
@Schema(description = "방 제목", example = "스터디 모임")
28+
private String roomTitle;
29+
30+
@Schema(description = "생성자 닉네임", example = "홍길동")
31+
private String createdByNickname;
32+
33+
@Schema(description = "만료 시간")
34+
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
35+
private LocalDateTime expiresAt;
36+
37+
@Schema(description = "활성 여부", example = "true")
38+
private boolean isActive;
39+
40+
@Schema(description = "유효 여부 (만료되지 않았는지)", example = "true")
41+
private boolean isValid;
42+
43+
public static InviteCodeResponse from(RoomInviteCode code) {
44+
return InviteCodeResponse.builder()
45+
.inviteCode(code.getInviteCode())
46+
.inviteLink("https://catfe.com/invite/" + code.getInviteCode())
47+
.roomId(code.getRoom().getId())
48+
.roomTitle(code.getRoom().getTitle())
49+
.createdByNickname(code.getCreatedBy().getNickname())
50+
.expiresAt(code.getExpiresAt())
51+
.isActive(code.isActive())
52+
.isValid(code.isValid()) // 만료 여부 체크
53+
.build();
54+
}
55+
}

0 commit comments

Comments
 (0)