Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bce7dbc
feat : 닉네임 중복 체크 (nickname unique), 인증코드 검사 예외처리 추가
yyytir777 Dec 8, 2025
3573613
fix : user_id IDENTITY strategy & dev redis host 이름변경 (localhost -> r…
yyytir777 Dec 8, 2025
08cfb63
test코드 생성 & swagger url 삭제 & 환경변수 중복 삭제
yyytir777 Dec 8, 2025
ac96fa0
fix : 엔드포인트 추가
yyytir777 Dec 9, 2025
43e75ed
Merge pull request #27 from tinybite-2025/feature/13-user
yyytir777 Dec 9, 2025
a70941b
Feature/26 notification (#29)
marshmallowing Dec 11, 2025
cbbf597
feat : google login 구현 완료
yyytir777 Dec 11, 2025
d03fd30
fix : main push 시에만 workflow trigger
yyytir777 Dec 11, 2025
4bc8ff6
feat : 파티 엔티티 정의
milowon Dec 21, 2025
3cb18ec
feat : 파티 dto
milowon Dec 21, 2025
f94eeb5
feat : party dto 정의
milowon Dec 21, 2025
d1bc8c3
feat : party entity 정의
milowon Dec 21, 2025
b2fb979
feat : 파티 생성,수정,삭제, 조회
milowon Dec 21, 2025
88d4fb6
feat : 거리 계산 클래스
milowon Dec 21, 2025
d4a9bf0
refactor : 불필요한 코드 삭제
milowon Dec 22, 2025
f21bc6b
refactor : token provider로 유저 아이디 추출하도록 변경
milowon Dec 22, 2025
bea737a
Fix: 파티 기능 버그 수정
milowon Dec 25, 2025
929f3b5
docs : 파티 swagger 문서 추가
milowon Dec 27, 2025
78576ea
feat : party, chat 엔티티
milowon Dec 27, 2025
da05f29
feat : party,chat enum
milowon Dec 27, 2025
9daf2f4
feat : party 기능
milowon Dec 27, 2025
0079372
feat : 파티 참여, 승인 기능
milowon Dec 30, 2025
1897296
Squashed commit of the following:
milowon Dec 31, 2025
8d7d3b4
docs : party controller swagger 문서
milowon Dec 31, 2025
db2e838
Merge branch 'main' into feature/party
milowon Dec 31, 2025
cccc27e
docs : party controller swagger 문서
milowon Dec 31, 2025
3928e56
Merge branch 'feature/party' of https://github.com/tinybite-2025/tiny…
milowon Dec 31, 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
2 changes: 1 addition & 1 deletion .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ jobs:
docker compose -f docker-compose.blue.yml down || true
docker compose -f docker-compose.blue.yml up -d

docker image prune -a -f
docker image prune -a -f
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ public AuthResponse refreshToken(RefreshTokenRequest request) {
@Transactional
public void logout(Long userId) {
refreshTokenRepository.deleteByUserId(userId);
userRepository.deleteById(userId);
log.info("로그아웃 - User ID: {}", userId);
}
Comment on lines 346 to 351
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

로그아웃 시 사용자를 삭제하지 마세요 - 치명적인 버그

Line 358에서 로그아웃 시 사용자를 데이터베이스에서 삭제합니다. 이는 매우 위험한 버그로, 다음과 같은 문제를 발생시킵니다:

  • 사용자의 모든 데이터 (파티 멤버십, 채팅 기록 등)가 손실됩니다
  • 외래 키 제약 조건 위반으로 인한 데이터 무결성 문제가 발생할 수 있습니다
  • 사용자가 단순히 로그아웃했을 뿐인데 계정이 삭제되어 사용자 경험에 심각한 문제가 생깁니다
  • 명시적인 회원 탈퇴 요청 없이 사용자 데이터를 삭제하면 GDPR/개인정보보호법 위반 가능성이 있습니다

로그아웃은 세션/토큰만 정리해야 하며, 사용자 데이터는 삭제하지 않아야 합니다. 회원 탈퇴는 별도의 엔드포인트로 처리해야 합니다.

🔎 수정 제안
     @Transactional
     public void logout(Long userId) {
         refreshTokenRepository.deleteByUserId(userId);
-        userRepository.deleteById(userId);
         log.info("로그아웃 - User ID: {}", userId);
     }

회원 탈퇴 기능이 필요한 경우, 별도의 메서드를 생성하세요:

@Transactional
public void withdrawUser(Long userId) {
    User user = userRepository.findById(userId)
            .orElseThrow(() -> BusinessException.of(UserErrorCode.USER_NOT_EXISTS));
    
    // 소프트 삭제: 상태를 WITHDRAW로 변경
    user.updateStatus(UserStatus.WITHDRAW);
    
    // 토큰 삭제
    refreshTokenRepository.deleteByUserId(userId);
    
    log.info("회원 탈퇴 - User ID: {}", userId);
}
🤖 Prompt for AI Agents
In src/main/java/ita/tinybite/domain/auth/service/AuthService.java around lines
355 to 360, the logout method currently deletes the User entity
(userRepository.deleteById(userId)) which must not happen on logout; change
logout to only remove session/tokens (delete refresh tokens) and log the event,
and move any account removal logic to a separate withdrawUser/withdrawal method
that performs a safe withdrawal (e.g., lookup user, set status to WITHDRAW or
soft-delete, remove tokens) and throws USER_NOT_EXISTS if the user is absent;
ensure logout is annotated @Transactional and does not call
userRepository.deleteById.


Expand Down
68 changes: 68 additions & 0 deletions src/main/java/ita/tinybite/domain/chat/entity/ChatRoom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ita.tinybite.domain.chat.entity;

import ita.tinybite.domain.chat.enums.ChatRoomType;
import ita.tinybite.domain.party.entity.Party;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "chat_room")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class ChatRoom {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "party_id", nullable = false)
private Party party;

@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private ChatRoomType type;

@Column(nullable = false, length = 100)
private String name;

@Column(nullable = false)
@Builder.Default
private Boolean isActive = true;

@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;

@OneToMany(mappedBy = "chatRoom", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<ChatRoomMember> members = new ArrayList<>();

// ========== 비즈니스 메서드 ==========

/**
* 멤버 추가
*/
public void addMember(ita.tinybite.domain.user.entity.User user) {
ChatRoomMember member = ChatRoomMember.builder()
.chatRoom(this)
.user(user)
.isActive(true)
.build();
members.add(member);
}

/**
* 채팅방 비활성화
*/
public void deactivate() {
this.isActive = false;
}
}
52 changes: 52 additions & 0 deletions src/main/java/ita/tinybite/domain/chat/entity/ChatRoomMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ita.tinybite.domain.chat.entity;

import ita.tinybite.domain.party.entity.Party;
import ita.tinybite.domain.user.entity.User;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Entity
@Table(name = "chat_room_member")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class ChatRoomMember {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_room_id", nullable = false)
private ChatRoom chatRoom;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "party_id")
private Party party;

@Column(nullable = false)
@Builder.Default
private Boolean isActive = true;

@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime joinedAt;

private LocalDateTime leftAt;

/**
* 퇴장
*/
public void leave() {
this.isActive = false;
this.leftAt = LocalDateTime.now();
}
}
14 changes: 14 additions & 0 deletions src/main/java/ita/tinybite/domain/chat/enums/ChatRoomType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ita.tinybite.domain.chat.enums;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public enum ChatRoomType {
ONE_TO_ONE("1:1 채팅"),
GROUP("단체 채팅");

private final String description;
}
14 changes: 14 additions & 0 deletions src/main/java/ita/tinybite/domain/chat/enums/ParticipantRole.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ita.tinybite.domain.chat.enums;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public enum ParticipantRole {
LEADER("리더", "파티를 생성하고 관리하는 리더"),
MEMBER("멤버", "일반 참가자");
private final String displayName;
private final String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ita.tinybite.domain.chat.repository;

import ita.tinybite.domain.chat.entity.ChatRoom;
import ita.tinybite.domain.chat.enums.ChatRoomType;
import ita.tinybite.domain.party.entity.Party;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface ChatRoomRepository extends JpaRepository<ChatRoom, Long> {

Optional<ChatRoom> findByPartyAndType(Party party, ChatRoomType type);

}
Loading