우선, 기본 세팅으로 Entity와 WebSocketConfig를 설정했습니다.
Entity
ChatRoom
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "chat_room")
public class ChatRoom extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "title", nullable = false)
@Size(min = 1, max = 100)
private String title;
@Column(name = "is_secret", length = 1, nullable = false)
private String isSecret;
}
ChatMessage
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "chat_message")
public class ChatMessage extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "message", columnDefinition = "TEXT")
@NotEmpty
private String message;
@ManyToOne
@JoinColumn(name = "chat_room_id", referencedColumnName = "id", nullable = false)
private ChatRoom chatRoom;
@ManyToOne
@JoinColumn(name = "employee_id", referencedColumnName = "id", nullable = false)
private Employee employee;
public void updateMessage(String message) {
this.message = message;
}
}
ChatRoom Entity나 ChatMessage Entity는 크게 어려움 없이 만들어줬습니다.
이 부분도 설정한 ERD에 맞춰서 원하는 대로 하면 될 듯 합니다...
Chat & ChatId
Chat
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "chat")
public class Chat {
@EmbeddedId
private ChatId id;
@MapsId("chatRoomId")
@ManyToOne(targetEntity=ChatRoom.class)
private ChatRoom chatRoom;
@MapsId("employeeId")
@ManyToOne(targetEntity=Employee.class)
private Employee employee;
}
@EmbeddedId로 ChatId가 기본키라는 것을 알려줍니다.
@MapsId로 식별자 클래스의 기본키인 chatRoomId와 employeeId를 잡아줬습니다.
@MapsId를 사용하지 않으면 오류가 나거나 하기도 합니다...
저는 이걸 안 잡아줬을 때 각각의 외래키가 2개씩... 중복되어 생겼었습니다.
ChatId
@Getter
@Setter
@Embeddable
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatId implements Serializable {
@JoinColumn(name = "chat_room_id")
private Long chatRoomId;
@JoinColumn(name = "employee_id")
private Long employeeId;
// equals와 hashCode()...
}
임베디드 타입을 사용하기 위해 ChatId 클래스를 만들고 @Embeddable를 붙여줬습니다.
또한, 직렬화하기 위해서 Serializable를 implements 했습니다.
💡 식별자 클래스는 다음의 내용을 만족해야 합니다
- @Embeddable 어노테이션 사용
- Serializable 인터페이스 구현
- equals, hashCode 구현
- 기본 생성자 필요
- 식별자 클래스의 접근 제어자 범위 : public
이런 식으로 복합키를 설정해줬습니다.
WebSocketConfig
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// stomp 접속 주소 url = ws://localhost:8080/ws
registry.addEndpoint("/ws") // socket 연결 엔드포인트
.setAllowedOrigins("http://localhost:3000") // CORS 허용범위
.withSockJS(); // 브라우저 호환성을 위해
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue"); // 메시지를 구독 요청하는 엔드 포인트(public, private)
registry.setApplicationDestinationPrefixes("/app"); // 메시지를 발행하는 엔드 포인트
}
Security 적용 전 기본적인 WebSocketConfig 설정입니다.
registerStompEndpoints
📌 addEndpoint
addEndpoint는 socket 연결 엔드포인트입니다.
React에서 웹소켓으로 연결할 때 주석에 적힌 것처럼 new SockJS('http://localhost:8080/ws') 이런 식으로 적어주면 됩니다.
📌 setAllowedOrigins
setAllowedOrigins는 CORS 허용범위로 setAllowedOrigins("*") 라고 적으면 모든 CORS 요청을 허용하는 것입니다.
저는 Security를 써서 프론트와 연결하기 위해 .setAllowedOrigins("http://localhost:3000")라고 적었습니다.
주소를 더 추가하고 싶다면 .setAllowedOrigins("http://localhost:3000", "http://domain.com", ...) 이처럼 뒤에 추가하면 됩니다.
📌 withSockJS
WebSocket을 지원하지 않는 브라우저에서도 WebSocket 요청을 연결하기 위해서 사용합니다.
더불어 React에서 연결할 때 new SockJS('http://localhost:8080/ws')로 http 연결이 가능합니다만,
witSockJS가 없을 경우 ws://localhost:8080/ws로 ws 연결을 해줘야 합니다.
configureMessageBroker
📌 enableSimpleBroker("/topic", "/queue")
주석에 적힌대로 메시지를 구독 요청하는 엔드 포인트(public, private)입니다.
백엔드의 ChatMessageController에서 사용했습니다.
topic은 여러 명의 채팅을, queue는 일대일 채팅 정도로 생각하면 됩니다.
원래 채팅 시스템을 만들 때 일대일 채팅도 추가하기 위해서 /queue까지 넣었지만........
📌 setApplicationDestinationPrefixes("/app")
메시지를 발행하는 엔드 포인트입니다.
React에서 백엔드로 메시지를 보낼 때 사용했습니다.
'자바&스프링' 카테고리의 다른 글
[Spring] WebSocket & STOMP로 채팅 구현하기 (4) (0) | 2025.04.07 |
---|---|
[Spring] WebSocket & STOMP로 채팅 구현하기 (3) (0) | 2025.03.28 |
[Spring] Spring boot와 MySQL 연동하기 (0) | 2025.03.20 |
[Spring] WebSocket & STOMP로 채팅 구현하기 (1) (0) | 2025.03.14 |