본문 바로가기
Spring Boot (프로젝트)

[스프링 부트] 프로젝트 4 백엔드 구현

by Hoozy 2023. 4. 2.

이전 게시글 프로젝트 3 엔티티, 비밀번호 암호화

https://hoozy.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3-%EC%97%94%ED%8B%B0%ED%8B%B0-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94

 

[스프링 부트] 프로젝트 3 엔티티, 로그인 (비밀번호 암호화)

이전 게시글 프로젝트 2 프론트엔드 구현 https://hoozy.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B5%AC%ED%98%84 테이블과 매칭된 엔티티를 생성. 회원가입할 때

hoozy.tistory.com

 

Application

  • @RequiredArgsConstructor : final 의 생성자를 자동으로 생성해줘서 @Autowired 사용 없이 의존성 주입 가능하게 하는 어노테이션

자바의 @Scheduled

  • @Scheduled 어노테이션은 프로젝트 Application에 @EnableScheduling 어노테이션을 추가해야 합니다.
  • 옵션이 fixedRate, fixedDelay, cron 세 가지가 있지만, 여기서는 cron 만 알아보겠습니다.
  • @Scheduled(cron = "0 0 0 * * *") 형태로 쓰며, 왼쪽부터 초 분 시간 일 월 요일 순으로 넣을 수 있다.
  • 위 이미지는 매일 모든요일 00시 0분 0초 마다 반복한다는 의미를 지닌다.
  • 위의 이미지는 스케쥴러로 매일 오늘의 문제를 랜덤으로 생성해서 today 테이블로 넣는 과정이다.

아래에는 Controller만 설명했습니다. 자세한 코드는 github에 올려놓았습니다.

https://github.com/hoozyz

홈페이지

HomeController

  • loggedInUsers 는 서버의 세션들의 리스트이다. -> 나중에 따로 자세하게 설명하겠습니다.
  • 타임리프 form 의 th:object 에 user를 사용하려면 model에 user의 빈 객체를 생성해서 보내줘야 user를 인식해서 에러가 안난다.
  • 세션에서 loginUser의 해당하는 세션들을 가져와서 로그인 한 유저들의 리스트를 뽑아서 보내준다.

  • 주석은 application의 오늘의 문제 생성 코드와 같으며, DB를 처음 생성했을때만 쓰는 코드이다.
  • 아래 list는 today 테이블에서 오늘의 문제 카테고리 별 3문제씩 리스트를 가져와서 넣고, 보내준다.

지식 페이지

KnowController

  • know 테이블에서 카테고리 별 모든 지식의 이름만 가져와서 보내기. -> 이름 클릭 시 ajax로 정보 보이게 구현.

  • check 가 1 일 때에는, 좋아요 추가 -> 좋아요 원래 값 + 1 로 update. check 가 0 일 때에는, 좋아요 제거. -> 좋아요 원래 값 - 1 로 update.
  • 문제 바꾸기는 만약 no3이 0 이면 현재 카테고리의 문제가 2개이기 때문에 주관식 문제로 인식하고 현재 바꾸기를 클릭한 카테고리 문제 2개 번호를 가져와 2개 번호를 제외한 같은 카테고리 중의 랜덤 문제를 한 개 가져온다. no3 이 0 이아니면 단답형으로 인식하고 같은 방식으로 랜덤으로 문제를 가져온다.

ReplyController

  • 지식의 번호로 지식의 댓글을 모두 가져와서 ajax로 보내는 메소드.
  • 모댓글 작성 메소드는 지식 번호와 내용을 가져와서 no는 마지막 auto_increment 번호를 가져와서 + 1 해서 넣어주고, 넣은 댓글 정보를 가져온다.

  • 답글은 현재 지식 번호와 모댓글의 번호, 내용을 가져와서 repn에 모댓글 번호를 넣고, 레벨을 1로 만들고, repo는 모댓글의 답글 중 순서여서 현재 모댓글의 번호를 가지고 답글의 개수를 가져와서 + 1 해서 넣는다.
  • 수정은 현재 댓글 번호와 내용을 가져와서 바꾼 후 보내준다.

  • 모댓글의 번호인 repn에 현재 모댓글의 번호를 넣어서 답글 리스트를 list에 넣고 보내준다.

문제(단답형) 페이지

ProbController

  • 홈페이지의 코드와 같다. -> 프론트의 코드만 다름.

문제(주관식) 페이지

ProbController

  • 주관식 문제에는 테이블 형식 문제가 있어서 만약 랜덤으로 가져온 2개의 문제 중에서 추가 정보가 있는 know 의 번호가 있을 때 이 번호로 addi 테이블에서 추가 정보들을 가져와서 프론트로 보내준다.
  • 이때 프론트에서 가져오기 위해 addiList에 카테고리 상관없이 넣는다. -> 카테고리 나누어서는 하기 힘들어서 카테고리 없이 가져가서 프론트에서 문제 번호로만 가져오도록 설계했습니다.

오픈 채팅 페이지

RoomController

  • form 태그의 th:field에 객체를 넣으려면 빈 객체를 넣어서 가야하기 때문에, 빈 객체를 넣고, 현재 room 테이블의 모든 채팅방을 가져와서 보내준다.
  • 채팅방 검색과 채팅방 생성은 ajax로 보내준다.

  • get 메소드는 roomid로 room 정보 가져오는 메소드이다.
  • delete 메소드는 room을 삭제하는데 이 때 이 채팅방의 채팅들을 다 삭제해야 한다.
    • 테이블의 값을 삭제할 때 참조하는 테이블이 있으면 참조하는 테이블 값까지 삭제해야 db 공간을 절약할 수 있다.

채팅을 알아보기 전에 웹소켓과 STOMP를 먼저 알아보자

웹소켓(WebSocket)이란,

  • 클라이언트가 HTTP 프로토콜을 통해 서버로 요청을 보내야 응답을 받을 수 있기 때문에, 이를 해결하기 위해 서버와 클라이언트 사이에 양방향 소통이 가능하게 개발된 프로토콜이다. 일반 Socket 통신과 달리 HTTP 80 Port를 사용하므로 방화벽에 제약이 없다. 접속까지는 HTTP 프로토콜을 이용하고, 그 이후 통신은 자체적인 웹소켓 프로토콜로 통신한다.

웹소켓 작동원리

  • 서버와 클라이언트간의 웹소켓 연결은 HTTP 프로토콜을 기반으로 이루어지고, 성공적으로 이루어지면, 서로 간에 웹소켓 연결(TCP/IP 기반)이 이루어지고 일정 시간 이후에 HTTP 연결은 자동으로 끊어진다.
  1. 클라이언트에서 서버로 TCP/IP 접속 요청
  2. 서버가 TCP/IP 접속 수락
  3. 클라이언트에서 서버로 웹소켓 열기(핸드쉐이크) 요청
  4. 서버가 웹소켓 열기 수락
  5. 서로 간의 웹소켓 데이터 송,수신

STOMP

  • Simple Text Oriented Messaging Protocol의 약자로, 메시징 전송을 효율적으로 하기 위해 만들어진 프로토콜이다.
  • 기본적으로 pub / sub 구조로 되어있어, 메시지를 전송하고 메시지를 받아 처리하는 부분이 확실히 정해져 있어서 개발자 입장에서 명확하게 인지하고 개발이 용이하다.
  • 정리하면, 웹소켓 위에서 동작하는 프로토콜로서 클라이언트와 서버가 전송할 메시지의 유형, 형식, 내용들을 정의하는 매커니즘이다.

ChatConfig

  • 웹소켓과 sockjs를 사용하려면 gradle에 추가해야 한다.

  • 스프링에서 지원하는 stomp를 사용하려면 @EnableWebSocketMessageBroker 어노테이션을 붙인 후, WebSocketMessageBrokerConfigurer 인터페이스를 구현한다.
  • registerStompEndPoints 메소드는 클라이언트에서 서버의 웹소켓에 연결할 때 API경로를 /ws/chat를 경로로 설정해준다는 의미이다. setAllowedOriginPatterns는 CORS 정책에 의해 origin이 다를 때 리소스 교환을 못할 때 origin을 *로 전체로 설정해서 url이 어디든 리소스를 교환할 수 있다는 의미이다.
    • CORS : 보안 목적으로 클라이언트와 서버의 origin(서버의 기본 url 주소와 포트까지의 주소)이 같을 때 리소스를 공유할 수 있다는 정책이다.
  • configureMessageBroker 메소드의 enableSimpleBroker는 채팅방 구독(.subscribe)할 때 메시지 브로커를 /queue, /topic으로 설정한다는 의미이다.
    • /queue : 1:1 교환할 때 사용. -> 1대1 채팅일 때
    • /topic : 1:N 교환할 때 사용. -> 채팅방 전체 메시지
  • setApplicationDestinationPrefixes 는 위와 아래 예시에서는 mapping 앞에 /app을 붙여서 /app/chat/join 으로 메시지를 .send 했을 때 ChatController의 join 메소드에 전달된다.

ChatController

  • join 메소드에서 클라이언트에서 .send로 메시지에 정보를 담아서 보내면, 룸 id를 가져와서 채팅방 정보를 가져오고, 채팅방의 최근 5개의 채팅 정보를 가져와서 '/'로 구분하여 문자열로 만든 후 먼저 넘겨주고, 환영 메시지를 마지막에 전송해준다.
  • FIRST 타입은 기존 DB에 있는 최근 채팅 5개 타입이고, JOIN 타입은 구독자가 들어올 때만 나오는 환영 메시지 타입이다.
  • SimpMessagingTemplate 객체에서 convertAndSend 메소드는 /topic/chat/room+roomid 채팅방에 구독한 구독자에게 전체 메시지인 chat을 보내는 것이다.

  • leave 메소드는 구독자가 나갈 때에만 메시지 타입인 LEAVE 타입의 chat을 /topic으로 보내준다.
  • message 메소드는 구독자가 메시지를 보내면 그 메시지의 내용과 작성자를 받고, /topic으로 보내주고, create 메소드로 현재 시간을 입력해서 DB에 저장한다.

기타

LoggedInUsersListener

  • 현재 서버의 들어가 있는 로그인 세션 리스트 가져오는 클래스 -> HttpSessionLister 인터페이스를 구현해야 한다.
  • 로그인 할 때마다 서버의 세션 목록에 로그인 세션 넣기
  • 로그아웃 할 때마다 서버의 세션 목록에서 로그인 세션 제거하기
  • 사용법은 각 컨트롤러처럼 sessions의 session에서 getAttribute("로그인 세션 이름"); 으로 가져와서 User 객체의 리스트로 넣어서 보내면 된다.

ImageController

  • 서버 외부의 로컬(현재 서버가 있는 컴퓨터) 폴더에 이미지를 저장했기 때문에, 외부에서 로컬에 접근하는 대신 img 태그의 src 속성 내부의 /upload/images?file=파일이름 로 접근했을 때 파일 명을 받아서 로컬 이미지 폴더의 파일이름에 해당하는 이미지를 파일 스트림을 사용해 바이트로 가져와서 저장한다.
  • 이후 이미지의 바이트 배열을 읽어서 ByteArrayOutPutStream 에 저장 후 이 baos에 저장된 바이트 배열을 fileArray 배열에 저장해 rest api로 반환해 img 태그에 이미지가 뜨게 하는 컨트롤러이다.

다음 게시글 프로젝트 5 S3 관련 설정 추가

https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-5-S3-%EA%B4%80%EB%A0%A8-%EC%84%A4%EC%A0%95-%EC%B6%94%EA%B0%80

 

[스프링 부트] 프로젝트 5 S3 관련 설정 추가

이전 게시글 프로젝트 4 백엔드 구현 https://hoozy.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B5%AC%ED%98%84 회원의 프로필 사진을 EC2 인스턴스에서 업로드 하고 다운로드 하기

hoozy.tistory.com

참고 자료

https://dev-gorany.tistory.com/212
https://dev-gorany.tistory.com/235

댓글