Skip to content
Merged
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
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ dependencies {

// WebClient
implementation 'org.springframework.boot:spring-boot-starter-webflux'

// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
}

tasks.named('test') {
Expand Down
9 changes: 0 additions & 9 deletions checkstyle/config/rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,6 @@ The following rules in the Naver coding convention cannot be checked by this con
<module name="TreeWalker">
<!-- Start of Naming chapter -->

<!-- [list-uppercase-abbr] -->
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1"/>
<message key="abbreviation.as.word"
value="[list-uppercase-abbr] Abbreviation in name ''{0}'' must contain no more than {1}"/>
<property name="allowedAbbreviations" value="DAO,BO,MySQL,SQL,OIDC,RSA"/>
</module>

<!-- [package-lowercase] -->
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
Expand Down
62 changes: 62 additions & 0 deletions src/main/java/com/rabbitmqprac/application/api/AuthApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.rabbitmqprac.application.api;

import com.rabbitmqprac.application.dto.auth.req.AuthSignInReq;
import com.rabbitmqprac.application.dto.auth.req.AuthSignUpReq;
import com.rabbitmqprac.application.dto.auth.req.AuthUpdatePasswordReq;
import com.rabbitmqprac.application.dto.user.res.UserIdRes;
import com.rabbitmqprac.domain.context.auth.exception.AuthErrorCode;
import com.rabbitmqprac.domain.context.user.exception.UserErrorCode;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanation;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanations;
import com.rabbitmqprac.infra.security.authentication.SecurityUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.Map;

@Tag(name = "[인증 API]")
public interface AuthApi {

@Operation(summary = "일반 회원가입")
@ApiExceptionExplanations({
@ApiExceptionExplanation(
errorCode = AuthErrorCode.class,
constants = "PASSWORD_CONFIRM_MISMATCH"
),
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "CONFLICT_USERNAME"
)
})
ResponseEntity<UserIdRes> signUp(@RequestBody @Validated AuthSignUpReq authSignUpReq);

@Operation(summary = "일반 로그인")
@ApiExceptionExplanations({
@ApiExceptionExplanation(
errorCode = AuthErrorCode.class,
constants = "INVALID_PASSWORD"
),
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "NOT_FOUND"
)
})
ResponseEntity<UserIdRes> signIn(@RequestBody @Validated AuthSignInReq authSignInReq);

@Operation(summary = "비밀번호 변경")
@ApiExceptionExplanations({
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "NOT_FOUND"
),
@ApiExceptionExplanation(
errorCode = AuthErrorCode.class,
constants = "INVALID_PASSWORD"
)
})
void patchPassword(@AuthenticationPrincipal SecurityUserDetails user, @RequestBody @Validated AuthUpdatePasswordReq authUpdatePasswordReq);
}
95 changes: 95 additions & 0 deletions src/main/java/com/rabbitmqprac/application/api/ChatMessageApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.rabbitmqprac.application.api;

import com.rabbitmqprac.application.dto.chatmessage.req.ChatMessageReq;
import com.rabbitmqprac.application.dto.chatmessage.res.ChatMessageDetailRes;
import com.rabbitmqprac.domain.context.chatroom.exception.ChatRoomErrorCode;
import com.rabbitmqprac.domain.context.user.exception.UserErrorCode;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanation;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanations;
import com.rabbitmqprac.infra.security.authentication.SecurityUserDetails;
import com.rabbitmqprac.infra.security.principal.UserPrincipal;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Tag(name = "[채팅 메시지 API]")
public interface ChatMessageApi {

@RequestMapping(method = RequestMethod.OPTIONS)
@Operation(
summary = "채팅 메시지 전송 (STOMP 설명용)",
description = """
STOMP를 통해 채팅 메시지를 전송하는 방법을 설명합니다. ※ 이 API는 HTTP가 아닌 WebSocket(STOMP) 기반으로 실제로 동작하지 않습니다.
- STOMP Destination: /pub/chat.room.{chatRoomId}/message
- Payload: ChatMessageReq {"content": "메시지 내용"}
- 인증: Header에 JWT 토큰 필요
"""
)
@ApiExceptionExplanations({
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "NOT_FOUND"
),
@ApiExceptionExplanation(
errorCode = ChatRoomErrorCode.class,
constants = "NOT_FOUND"
)
})
void sendMessage(UserPrincipal principal,
@DestinationVariable Long chatRoomId,
@Validated ChatMessageReq message
);

@Operation(summary = "채팅 메시지 목록 조회 (이전)", description = "특정 채팅 메시지 ID 이전의 채팅 메시지 목록을 조회한다.")
@Parameters({
@Parameter(
name = "lastChatMessageId",
description = """
조회 시작 기준이 되는 채팅 메시지 ID. 이 ID 이전의 메시지들을 조회한다.
기본값은 0이며, 이 경우 가장 최근 메시지부터 조회를 시작한다.
""",
example = "0"
),
@Parameter(
name = "size",
description = "한 번에 조회할 채팅 메시지의 최대 개수. 기본값은 30이다.",
example = "30"
)
})
List<ChatMessageDetailRes> readChatMessagesBefore(
@PathVariable Long chatRoomId,
@RequestParam(value = "lastChatMessageId", defaultValue = "0") Long lastMessageId,
@RequestParam(value = "size", defaultValue = "30") int size
);

@Operation(summary = "채팅 메시지 목록 조회 (범위)")
@Parameters({
@Parameter(
name = "from",
description = "조회할 채팅 메시지 ID 범위의 시작. 이 ID를 포함한다.",
example = "100",
required = true
),
@Parameter(
name = "to",
description = "조회할 채팅 메시지 ID 범위의 끝. 이 ID를 포함한다.",
example = "200",
required = true
)
})
List<ChatMessageDetailRes> readChatMessagesBetween(@AuthenticationPrincipal SecurityUserDetails user,
@PathVariable Long chatRoomId,
@RequestParam Long from,
@RequestParam Long to
);
}
35 changes: 35 additions & 0 deletions src/main/java/com/rabbitmqprac/application/api/ChatRoomApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.rabbitmqprac.application.api;

import com.rabbitmqprac.application.dto.chatroom.req.ChatRoomCreateReq;
import com.rabbitmqprac.application.dto.chatroom.res.ChatRoomDetailRes;
import com.rabbitmqprac.application.dto.chatroom.res.ChatRoomSummaryRes;
import com.rabbitmqprac.domain.context.user.exception.UserErrorCode;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanation;
import com.rabbitmqprac.infra.security.authentication.SecurityUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@Tag(name = "[채팅방 API]")
public interface ChatRoomApi {

@Operation(summary = "채팅방 생성")
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "NOT_FOUND"
)
ChatRoomDetailRes create(
@AuthenticationPrincipal SecurityUserDetails user,
@RequestBody @Validated ChatRoomCreateReq chatRoomCreateReq
);

@Operation(summary = "가입한 채팅방 목록 조회", description = "로그인된 유저의 채팅방 목록을 조회한다.")
List<ChatRoomDetailRes> getMyChatRooms(@AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "채팅방 목록 조회")
List<ChatRoomSummaryRes> getChatRooms(@AuthenticationPrincipal SecurityUserDetails user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.rabbitmqprac.application.api;

import com.rabbitmqprac.application.dto.chatroommember.res.ChatRoomMemberDetailRes;
import com.rabbitmqprac.infra.security.authentication.SecurityUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.List;

@Tag(name = "[채팅방 멤버 API]")
public interface ChatRoomMemberApi {

@Operation(summary = "채팅방 참여")
void joinChatRoom(
@AuthenticationPrincipal SecurityUserDetails user,
@PathVariable Long chatRoomId
);

@Operation(summary = "채팅방 멤버 목록 조회")
List<ChatRoomMemberDetailRes> getChatRoomMembers(@PathVariable Long chatRoomId);
}
68 changes: 68 additions & 0 deletions src/main/java/com/rabbitmqprac/application/api/OauthApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.rabbitmqprac.application.api;

import com.rabbitmqprac.application.dto.oauth.req.OauthSignInReq;
import com.rabbitmqprac.application.dto.oauth.req.OauthSignUpReq;
import com.rabbitmqprac.application.dto.user.res.UserIdRes;
import com.rabbitmqprac.domain.context.oauth.exception.OauthErrorCode;
import com.rabbitmqprac.domain.context.user.exception.UserErrorCode;
import com.rabbitmqprac.domain.persistence.oauth.constant.OauthProvider;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanation;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanations;
import com.rabbitmqprac.infra.security.exception.JwtErrorCode;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "[소셜 인증 API]")
public interface OauthApi {

@Operation(
summary = "소셜 로그인",
description = """
code는 각 소셜 플랫폼에서 발급받은 인가 코드로 KAKAO, GOOGLE 등의 OauthProvider를 통해 발급받은 인가 코드를 의미합니다.
- [KAKAO CODE 발급 URL](https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=57e328d97dc0c7b53898e2e79082f23c&redirect_uri=http://localhost:8080&scope=openid%20profile_nickname&nonce=example-nonce)
- [GOOGLE CODE 발급 URL](https://accounts.google.com/o/oauth2/v2/auth?client_id=248388975343-0oo0f79rrsqpf1k63ahpivkhd2rfu1jp.apps.googleusercontent.com&redirect_uri=http://localhost:8080&response_type=code&scope=openid%20email%20profile&nonce=example-nonce)
"""
)
@ApiExceptionExplanations({
@ApiExceptionExplanation(
errorCode = OauthErrorCode.class,
constants = {"MISSING_ISS", "INVALID_ISS", "MISSING_NONCE", "INVALID_ISS", "INVALID_AUD", "INVALID_NONCE"}
),
@ApiExceptionExplanation(
errorCode = JwtErrorCode.class,
constants = "MALFORMED_TOKEN"
)
})
ResponseEntity<UserIdRes> signIn(
@RequestParam OauthProvider oauthProvider,
@RequestBody @Validated OauthSignInReq req
);


@Operation(
summary = "소셜 회원가입",
description = """
code는 각 소셜 플랫폼에서 발급받은 인가 코드로 KAKAO, GOOGLE 등의 OauthProvider를 통해 발급받은 인가 코드를 의미합니다.
- [KAKAO CODE 발급 URL](https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=57e328d97dc0c7b53898e2e79082f23c&redirect_uri=http://localhost:8080&scope=openid%20profile_nickname&nonce=example-nonce)
- [GOOGLE CODE 발급 URL](https://accounts.google.com/o/oauth2/v2/auth?client_id=248388975343-0oo0f79rrsqpf1k63ahpivkhd2rfu1jp.apps.googleusercontent.com&redirect_uri=http://localhost:8080&response_type=code&scope=openid%20email%20profile&nonce=example-nonce)
"""
)
@ApiExceptionExplanations({
@ApiExceptionExplanation(
errorCode = OauthErrorCode.class,
constants = {"CONFLICT", "MISSING_ISS", "INVALID_ISS", "MISSING_NONCE", "INVALID_ISS", "INVALID_AUD", "INVALID_NONCE"}
),
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "CONFLICT_USERNAME"
)
})
ResponseEntity<UserIdRes> signUp(
@RequestParam OauthProvider oauthProvider,
@RequestBody @Validated OauthSignUpReq req
);
}
44 changes: 44 additions & 0 deletions src/main/java/com/rabbitmqprac/application/api/UserApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.rabbitmqprac.application.api;

import com.rabbitmqprac.application.dto.user.res.UserDetailRes;
import com.rabbitmqprac.application.dto.user.req.NicknameCheckReq;
import com.rabbitmqprac.application.dto.user.req.NicknameUpdateReq;
import com.rabbitmqprac.domain.context.user.exception.UserErrorCode;
import com.rabbitmqprac.global.annotation.ApiExceptionExplanation;
import com.rabbitmqprac.infra.security.authentication.SecurityUserDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;
import java.util.Map;

@Tag(name = "[유저 API]")
public interface UserApi {

@Operation(summary = "내 정보 조회")
UserDetailRes getMember(@AuthenticationPrincipal SecurityUserDetails user);

@Operation(summary = "전체 회원 조회")
List<UserDetailRes> getMembers();

@Operation(summary = "유저 아이디 중복 확인")
Map<String, Boolean> isDuplicatedUsername(@RequestParam @Validated String username);

@Operation(summary = "닉네임 변경")
@ApiExceptionExplanation(
errorCode = UserErrorCode.class,
constants = "CONFLICT_USERNAME"
)
void patchNickname(
@AuthenticationPrincipal SecurityUserDetails user,
@RequestBody NicknameUpdateReq nicknameUpdateReq

);

@Operation(summary = "닉네임 중복 확인")
Map<String, Boolean> checkNicknameDuplication(@Validated NicknameCheckReq nicknameCheckReq);
}
Loading
Loading