본문 바로가기
experiences/Hackathon||Project

프로젝트 정리 및 회고: Omok Project

by wingbeat 2024. 6. 10.

미니프로젝트 Omok Project

https://github.com/Jyebin/shinhan_team3_omok/blob/main/README.md

 

shinhan_team3_omok/README.md at main · Jyebin/shinhan_team3_omok

Contribute to Jyebin/shinhan_team3_omok development by creating an account on GitHub.

github.com

 

 

목표 

1. 오목게임을 웹 버전으로 개발한다.

2. 웹소켓, AJAX, JSP, Servlet을 적용시켜 개발한다.

3. DB를 연동하여 사용자 정보와 게임 기록을 관리한다.

4. 팀원 개개인이 기능별로 개발을 담당하여 프론트, 백엔드를 모두 경험해본다.

 

주요 기능

1. 사용자 인증 : 로그인 회원가입 기능을 통한 사용자 인증 및 탈퇴, 로그아웃 기능 구현

2. 실시간 대전 : 웹소켓을 활용해 실시간 오목 대전 기능 구현

3. 랜덤 입장 기능: 생성된 공개방에 빠른 입장으로 게임 상대를 매칭해주는 기능 구현

4. 사용자 커스텀 입장 기능 : 생성된 비공개방에 랜덤으로 발급되는 코드를 통해 원하는 상대와 게임할 수 있는 기능 구현

5. 실시간 채팅 : 웹소켓을 활용한 실시간 오목 대전 중 채팅 기능 구현

6. 랭킹 시스템 : 게임 기록을 DB에 저장하여 게임 결과에 따른 사용자 랭킹 시스템 구현

 

이 기능들 중 내가 맡은 부분은

1. 사용자 인증 부분에서 사용자 탈퇴, 로그아웃 기능 구현

2. 실시간 채팅 기능 구현 (웹소켓 기반)

3. 랜덤/커스텀 입장 기능의 코드 리팩토링

4. 깃 프로젝트 세팅, 리드미 작성, 노션 작성

 

프로젝트 진행 과정

1. 기능 확정 및 역할 분담

2. 와이어 프레임 제작

3. 디자인 만들기

4. 웹 퍼블리싱

 


프로젝트 디자인 패턴 : MVC (Model-View-Controller)

Model

- DAO (Data Access Object) : DB 상호 작용 담당. DB에서 데이터 조회, 저장하는 기능을 캡슐화

- VO (Value Object) : DB에서 조회한 데이터를 담는 객체. 주로 데이터 전송 객체로 사용

View

- JSP (JavaServer Pages) : 사용자에게 보여질 화면 생성. HTML + Java 혼합하여 동적 웹페이지 생성

- AJAX : 클라이언트 측에서 비동기적으로 서버와 통신, 서버로 부터 받은 데이터를 동적으로 화면에 반영

Controller

- Servlet : 클라이언트 요청을 받아 처리하고, 필요한 비즈니스 로직을 수행 후 결과를 JSP에 전달하여 화면 생성. Model과 View 사이의 상호작용 관리

- WebSocket : 실시간 양방향 통신이 필요한 경우 사용. 웹소켓 서버는 클라이언트와 실시간으로 데이터를 주고 받음.

 

MVC 패턴 프로젝트의 흐름

1. 클라이언트 요청

- 사용자가 웹 브라우저에서 특정 요청 (클릭, 폼제출 등)을 한다.

2. 컨트롤러 (Servlet)

- 클라이언트의 요청을 받아 처리. 요청에 따라 적절한 비즈니스 로직 수행

- 웹소켓으로 실시간 데이터 처리

3. 모델 (DAO, VO)

- 비즈니스 로직 수행 중 DB와의 상호작용이 필요하면 DAO로 데이터 조회 또는 저장.

- DB에서 조회한 데이터는 VO 객체에 담겨 컨트롤러로 전달됨

4. 뷰 (JSP, AJAX)

- 컨트롤러가 비즈니스 로직 수행 결과를 JSP에 전달해 사용자가 보는 화면을 생성

- AJAX는 클라이언트 측에서 비동기적으로 서버와 통신해 필요 데이터를 받아와 화면 일부를 업데이트 한다.


프로젝트 파일 구조

 

VO (Value Object)

GamelistVO, UserVO 각 DB내 테이블에 해당하는 VO

(ProfileVO는 구현 실패)

 

DAO (Data Access Object)

1. GameDAO

- createGame: 게임방 생성을 위한 INSERT into GameList

- changeIsCustom: Custom <-> Random 방 전환을 위해 정보를 불러오는 SELECT & UPDATE

 

2. LandingDAO

 

3. MainPageDAO

 

4. RandomGameDAO

 

5. UserDAO

 

6. WinLoseDAO

 

Servlet

1. LandingPageServlet

2. UserServlet


3. MainPageServlet

4. RankingServlet

5. LogoutServlet

 

6. CreateRoomServlet

 

7. CustomGameServlet

8. RandomGameServlet

 

9. EnterRoomServlet


10. WebSocket

11. GoBoard

12. WinLoseServlet

 

아쉬운 점

- 테스트용 socket.jsp, websocket 파일 포함

- GameDAO와 RandomDAO를 합칠 수 있었다.

- ProfileVO 삭제 필요

- 각자 기능을 개발하다보니 중복되게 생성되는 부분이 있어서 수정이 필요할 것 같다.

 


더보기

Keep: 프로젝트 완료 후에도 간직하고 싶은 잘했던 것 / 좋았던 것
*ex) "~기술 적용을 했더니 효율적으로 ~를 할 수 있었다."
Problem: 프로젝트 중 겪었던 어려움(기술, 소통, 협업, 에러 등 프로젝트 진행 관련된 그 어느것이든) / 프로젝트 완료 후에도 아쉬움으로 남는 것
*ex) "~기능 적용 중 ~이슈가 발생하였다."
Try: Problem 중 해결된 사항에 대한 해결 방법 / 해결되지 않은 사항에 대한 피드백
*ex) "~기능 적용 중 발생한 ~이슈 해결을 위해 ~를 하였다."

 

Keep : 프로젝트 완료 후에도 간직하고 싶은 잘했던 것 / 좋았던 것

1. 문서 작성을 잘 한듯.

이건 사실 그 전 프로젝트를 할 때도 칭찬받았는데, 리드미나 노션 등 정리를 잘 했던 것 같다.

 

2. Git Project를 이용해 칸반보드를 만들어 진행 상황을 공유

이것도 마찬가지로 내가 무엇을 했는지, 다른 사람이 무엇을 했는지 파악하기 쉬워서 프로젝트 진행에 도움됐다고 생각

 

Problem: 프로젝트 중 겪었던 어려움(기술, 소통, 협업, 에러 등 프로젝트 진행 관련된 그 어느것이든) / 프로젝트 완료 후에도 아쉬움으로 남는 것

1. 부족했던 나의 개발 실력. 하하;;

나는 사실 지금껏 레퍼런스 코드가 없으면 개발을 어려워했던 것 같다.

특히 그냥 개념만 배웠을 때는 이해를 하는데 이걸로 개발을 하려니 어려움이 많았다.

특히 같이 실시간 채팅을 개발한 팀원이 개발에 능숙해서 많이 배웠다. 

 

2. 코드의 전체적인 그림을 그리기 어려웠다.

이 부분은 지금부터라도 그려보며 이해를 해야겠다.

 

3. 웹소켓 등 각각의 개념은 이해했지만, 세션 등 세부 부분에서 어떤식으로 사용되는지, 내가 사용 해야 하는지를 이해하기가 어려웠다.

 

4. CSS 적용이 잘 되지 않았다. 쿠키 삭제를 하면 해결되는 부분도 있었다. 다만 내가 CSS를 어려워하는 것 같기도.

 

Try: Problem 중 해결된 사항에 대한 해결 방법 / 해결되지 않은 사항에 대한 피드백

1. Git 사용법

오랜만에 하다보니 까먹었다.

그냥 많이 커밋하다보니 감이 왔다. git branch, git push, merge, PR request 등...

다음에는 더 능숙하게 사용할 수 있도록. (여기서 은근 헤맴)

 

2. SweetAlert()

사용하는 거는 좋았는데 css랑 안맞는지 계속 팝업이 뜰 때 마다 main 사이즈가 바뀌는 일이 있었다. 이 부분을 바꾸지 못해서 아쉽지만 다음에는 유의해서 사용해보고 싶다.

 

3. 코드 리팩토링

한 게임 페이지에서 리팩토링을 했지만, 이 프로젝트의 코드가 랜덤, 커스텀으로 나뉘어서 아무래도 재사용되는 코드들은 Class로 캡슐화해서 재사용성을 높이는 방법을 썼다면 더 좋았을 것 같다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



MVC 패턴

이 프로젝트에서 각 기술 요소는 MVC 패턴의 구성요소와 다음과 같이 연결됩니다:

  1. Model: 사용자 정보 및 게임 상태와 같은 데이터와 비즈니스 로직을 관리합니다.
  2. View: JSP 파일을 통해 사용자 인터페이스를 렌더링하며, 게임 보드와 사용자 상호작용, 실시간 업데이트를 표시합니다.
  3. Controller: Java 서블릿이 사용자 요청을 처리하고 입력을 처리하여 모델을 업데이트합니다.

이 패턴은 관심사의 분리를 통해 모듈화와 유지보수성을 향상시킵니다.

  1. DAO (Data Access Object):
    • 역할: 데이터베이스에 접근하여 데이터를 조회하거나 조작하는 역할을 담당합니다.
    • MVC에서의 위치: Model 부분에 속하며, 비즈니스 로직과 데이터베이스 사이의 상호작용을 처리합니다.
  2. VO (Value Object):
    • 역할: 데이터를 객체 형태로 캡슐화하여 서블릿과 JSP 사이에서 데이터를 전달하는 역할을 합니다.
    • MVC에서의 위치: Model 부분에 위치하며, 데이터의 표현과 전송을 담당합니다.
  3. Servlet:
    • 역할: 클라이언트의 요청을 받아 처리하고, 그 결과를 JSP에 전달하거나 데이터베이스와의 상호작용을 DAO를 통해 수행합니다.
    • MVC에서의 위치: Controller 부분에 해당하며, 사용자의 요청을 분석하고 처리를 담당합니다.
  4. JSP:
    • 역할: 사용자에게 보여질 웹 페이지를 생성하며, 서블릿으로부터 받은 데이터를 활용하여 동적인 웹 페이지를 구성합니다.
    • MVC에서의 위치: View 부분에 속하며, 사용자 인터페이스와 관련된 처리를 담당합니다.
  5. JS (JavaScript) and CSS:
    • 역할: 웹 페이지의 동작과 스타일을 담당합니다. JavaScript는 클라이언트 측에서 동적인 기능을 제공하며, CSS는 웹 페이지의 스타일을 정의합니다.
    • MVC에서의 위치: View의 일부로, 사용자 경험과 인터페이스의 외관을 향상시킵니다.

MVC 패턴 사용 이유: MVC 패턴을 사용하는 주된 이유는 애플리케이션의 관심사를 명확하게 분리하여 각 부분의 독립적인 개발과 유지보수를 용이하게 하기 위함입니다. Model은 데이터와 비즈니스 로직에, View는 사용자 인터페이스에, Controller는 입력 처리와 흐름 제어에 집중함으로써, 각 부분이 서로에게 미치는 영향을 최소화하고 모듈성을 향상시킵니다.

 

 

프로젝트에서 사용된 MVC 패턴 구조

모델 (Model)

  • DAO (Data Access Object): 데이터베이스와 상호작용하여 데이터 CRUD(생성, 읽기, 업데이트, 삭제)를 처리합니다.
    • UserDAO: 사용자 정보 처리
    • GameDAO: 게임 방의 생성과 상태 변경을 위한 메서드를 제공하며, 주로 게임 방의 속성을 설정하고 변경하는 작업 (방 생성, 공개 방 전환, 비공개 방 찾기)
    • LandingDAO : Login 회원 정보 확인 처리
    • MainPageDAO : 메인 정보 처리 (회원 랭킹, ㅇ저 리스트)
    • RandomGameDAO : 게임 방 참가 여부를 확인하는 메서드를 제공하며, 특정 조건에 맞는 게임 방을 조회하고 그 상태를 업데이트하는 작업 

뷰 (View)

  • JSP 파일: 사용자에게 보여지는 웹 페이지를 생성합니다.
    • CustomGamePage.jsp, RandomGamePage.jsp: 게임 화면 및 실시간 채팅. 방 정보 변환
    • LandingPage.jsp: 로그인 진입 화면
    • MainPage.jsp : 로그인 후 랭킹 및 방 입장 가능한 공간
    • RegisterPage.jsp : 회원가입 화면
    • include > alert.jsp: 알림 화면 (sweetalert 사용)

컨트롤러 (Controller)

  • 서블릿 (Servlet): 사용자 요청을 처리하고, 모델과 뷰를 연결합니다.
    • CustomGameServlet, RandomGameServlet : 게임 관련 로직 처리
    • LogoutServlet: 로그아웃 처리
    • WebSocket : 실시간 게임 및 채팅 로직 처리 (type을 나눠서 진행)
    • GoBoard : 오목 게임 로직 처리

MVC 패턴 사용 이유

  1. 유지보수 용이성: 각 구성 요소가 독립적으로 동작하여, 예를 들어, 데이터베이스와 관련된 부분만 변경해도 다른 부분에는 영향을 미치지 않습니다. 수정이 필요한 부분만 변경하면 됩니다.
  2. 모듈화: 코드의 관심사를 분리하여 각 부분이 독립적으로 작동하도록 합니다. 기능별로 코드를 분리하여 각 부분의 역할을 명확히 합니다. 데이터베이스 접근, 비즈니스 로직, 사용자 인터페이스를 분리하여 코드의 가독성을 높입니다.
  3. 재사용성: 모델과 뷰는 재사용 가능하며, 새로운 컨트롤러를 추가하여 기능을 확장할 수 있습니다. 예를 들어, 새로운 게임 모드를 추가할 때 기존 모델과 뷰를 재사용할 수 있습니다.
  4. 협업 용이성: 프론트엔드 개발자와 백엔드 개발자가 독립적으로 작업할 수 있습니다. 이는 대규모 프로젝트에서 특히 중요합니다.

이 구조는 코드의 가독성과 유지보수성을 높이며, 대규모 프로젝트에서 특히 유용합니다.

 

 


WebSocket 통신

  • 기능: 서버와의 실시간 양방향 통신을 설정하여 게임 데이터를 실시간으로 주고받습니다.
  • 강조: 실시간 인터랙션과 통신의 안정성 및 신속성.

실시간 채팅

  • 기능: 게임 중 플레이어 간의 실시간 채팅을 가능하게 합니다.
  • 강조: 커뮤니케이션의 원활함과 게임 경험의 향상.

알림 기능

  • 기능: 다양한 상황에 맞는 알림을 SweetAlert를 사용하여 사용자에게 보여줍니다.
  • 강조: 사용자 경험의 직관성 및 인터페이스의 친절함.

Annotation

 

1. @Getter / @Setter

  • 기능: 자동으로 필드(변수)의 값을 가져오거나 설정하는 메소드를 만들어 줘요.
  • 예시: 만약 '나이'라는 필드가 있다면, @Getter는 나이를 알려주는 메소드를, @Setter는 나이를 바꾸는 메소드를 자동으로 만들어 줘요.

2. @WebServlet

  • 기능: 웹에서 사용하는 서블릿이라는 프로그램을 쉽게 만들 수 있게 도와줘요. 어떤 주소로 접속했을 때 이 서블릿을 사용할지 설정할 수 있어요.
  • 예시: 만약 "/home"이라는 주소로 접속하면, 이 서블릿이 작동하도록 설정할 수 있어요.

3. @Override

  • 기능: 이미 만들어진 메소드를 새로 고쳐서 다시 작성하고 싶을 때 사용해요. 이게 정말 제대로 새로 고쳐졌는지 컴퓨터가 확인해 줘요.
  • 예시: 부모가 만든 '말하기'라는 기능을 자녀가 다르게 말하는 방식으로 바꿀 때 사용해요.

4. @ServerEndpoint

  • 기능: 웹에서 서로 실시간으로 메시지를 주고받는 프로그램을 만들 때 사용해요.
  • 예시: 채팅 프로그램에서 채팅이 이루어지는 주소를 설정할 때 사용해요.

5. @OnOpen, @OnMessage, @OnClose, @OnError

  • 기능: 실시간 통신을 하는 프로그램에서, 연결이 시작될 때, 메시지가 올 때, 연결이 끝날 때, 오류가 발생했을 때 각각 어떻게 처리할지 정할 수 있게 해줘요.
  • 예시: 채팅방에 누군가 들어오거나 나갈 때, 메시지를 받았을 때, 오류가 생겼을 때 각각 다른 반응을 보여줄 수 있어요.

6. @PathParam

  • 기능: 인터넷 주소에 있는 정보를 가져와서 사용할 수 있게 해줘요.
  • 예시: "/user/{id}"라는 주소에서 {id} 부분에 들어간 숫자를 프로그램에서 사용자의 ID로 사용할 수 있게 해줘요.

이렇게 어노테이션은 코드를 더 깔끔하고 명확하게 작성할 수 있게 도와주고, 여러 복잡한 기능을 쉽게 추가할 수 있게 도와줍니다!


  1.  

데이터베이스 관련 기능

  1. ResultSet: 데이터베이스에서 쿼리 결과를 저장하고, 그 결과를 순차적으로 읽어오는 객체입니다. 데이터베이스에서 데이터를 받아와 처리하는 기능을 담당합니다.
    • 예시: 도서관에서 책 목록을 받아서 한 권씩 읽는 것과 비슷해요. 책을 페이지별로 넘기며 정보를 읽는 것처럼, 데이터베이스의 정보를 하나씩 접근할 수 있게 해줍니다.
  2. Connection: 데이터베이스와 애플리케이션 사이의 연결을 관리하는 객체입니다.
    • 예시: 컴퓨터와 인터넷을 연결하는 케이블과 같아요.  마치 전화선으로 전화기와 통신사를 연결하는 것과 같이, 데이터베이스와 컴퓨터 프로그램이 서로 소통할 수 있게 해줍니다.
  3. PreparedStatement: SQL 쿼리를 미리 컴파일해 놓고, 실행 시에 파라미터를 바인딩하여 사용합니다. 보안과 성능을 높여줍니다. 데이터베이스에 안전하게 명령을 보내는 기능입니다. 미리 정해진 형식에 데이터를 채워 넣어서 오류를 줄이고 보안을 강화할 수 있습니다. 
    • 예시: 미리 만들어둔 편지 서식에 이름과 주소만 적어서 보내는 것과 같아요. 
  4. COUNT(*): SQL 쿼리에서 특정 조건에 맞는 레코드(데이터)의 개수를 세는 함수입니다.
    • 예시: 예를 들어, 동물원에 있는 동물들의 수를 세는 것처럼 데이터의 수를 셀 수 있습니다.

 

웹 요청 및 응답 관련 기능

 

웹 서버와 클라이언트 간에 데이터를 주고받을 때 사용되는 객체입니다. 예를 들어, 학교에서 선생님이 학생에게 질문을 하고, 학생이 대답을 하는 것처럼 정보를 주고받을 수 있습니다.

 

  1. HttpServletRequest: 웹 브라우저에서 서버로 보내는 요청 정보를 담고 있는 객체입니다.
    • 예시: 친구에게 보낸 초대장과 같아요.
  2. HttpServletResponse: 서버에서 웹 브라우저로 보내는 응답 정보를 담고 있는 객체입니다.
    • 예시: 친구에게 받은 답장과 같아요.
  3. HttpSession: 사용자가 웹 사이트에 접속하는 동안 사용자의 정보를 서버에 저장할 수 있는 기능입니다.  .
    • 예시: 이것은 마치 놀이공원에 들어갈 때 팔찌를 받아 그 안에 자신의 정보를 담고 다니는 것과 비슷해요.
  4. setAttribute / getSession: HttpSession에 정보를 저장하거나 정보를 가져오는 기능입니다. 이를 통해 사용자의 정보를 필요할 때마다 쉽게 사용할 수 있습니다. 세션에 정보를 저장(setAttribute)하거나, 세션을 가져오는(getSession) 메서드입니다.
    • 예시: 보물 상자에 물건을 넣어두고(setAttribute), 나중에 그 상자를 열어 물건을 꺼내 보는(getSession) 것과 같아요.

 

서블릿 관련 기능

  1. @WebServlet: 서블릿을 특정 URL에 매핑하여 선언하는 어노테이션입니다.
    • 예시: 마치 학교에서 특정 교실로 가야 할 때 그 방의 번호를 알려주는 것과 비슷합니다.
  2. HttpServlet: (웹 요청을 처리하기 위한) 서블릿을 만들기 위해 사용하는 기본 클래스입니다. 이 클래스를 사용함으로써 웹 페이지에서 다양한 요청을 쉽게 처리할 수 있습니다.
    • 예시: 학교에서 기본적으로 제공되는 학용품 세트와 같아요.
  3. doGet / doPost: 웹 서버에 정보를 요청하거나 데이터를 전송할 때 사용하는 GET 요청과 POST 요청을 처리하는 메서드입니다. doGet은 데이터를 요청할 때, doPost는 데이터를 보낼 때 사용합니다.
    • 예시: doGet은 친구에게 "안녕?"하고 인사하는 것, doPost는 친구에게 선물을 주는 것과 비슷해요.
  4. sendRedirect: 사용자를 다른 웹 페이지로 자동으로 이동시키는 기능입니다. 사용자를 다른 URL로 리다이렉트하는 메서드입니다.
    • 예시: 도서관에서 책을 찾으려 했는데, 사서가 "저쪽 방에 가서 찾으세요"라고 안내하는 것과 같아요. 마치 게임을 하다가 다음 레벨로 넘어가는 버튼을 누르는 것과 같은 역할을 해요.

HTML 및 DOM 관련 기능

  1. document.querySelector: HTML 문서에서 특정 요소를 찾는 메서드입니다.
    • 예시: 책에서 특정 단어를 찾는 것과 같아요.
  2. createElement / appendChild: 새로운 HTML 요소를 만들고(createElement), 그것을 기존 요소에 추가(appendChild)하는 메서드입니다.
    • 예시: 종이 위에 새로운 그림을 그리고(createElement), 그 그림을 노트에 붙이는(appendChild) 것과 같아요.
  3. window.onload: 웹 페이지가 모두 로드되었을 때 실행되는 이벤트입니다.
    • 예시: 학교 종이 울리고 수업을 시작하는 것과 비슷해요.
  4. addEventListener: 특정 이벤트(예: 클릭, 키보드 입력)가 발생했을 때 실행할 함수를 등록하는 메서드입니다.
    • 예시: 친구가 벨을 누르면 문을 열어주는 것과 같아요.

JSON 및 데이터 전송 관련 기능

  1. JSON.parse: JSON 형식의 문자열을 JavaScript 객체로 변환하는 메서드입니다.
    • 예시: 암호문을 풀어 이해할 수 있는 문장으로 만드는 것과 같아요.
  2. XMLHttpRequest: 웹 페이지가 서버와 데이터를 주고받을 수 있게 해주는 객체입니다.
    • 예시: 우체국에서 편지를 보내고 받는 것과 같아요.

기타 유틸리티 기능

  1. SecureRandom: 예측할 수 없는 안전한 무작위 숫자를 생성하는 클래스입니다.
    • 예시: 마술사가 모자를 흔들어 무작위로 숫자를 뽑는 것과 같아요.
  2. StringBuilder: 문자열을 효율적으로 조작할 수 있게 해주는 클래스입니다. 여러 조각의 글자를 하나로 잘 연결하여 문장을 만들 때 사용합니다.
    • 예시: 여러 개의 레고 블록을 하나로 조립하는 것과 비슷해요. 
  3. setContentType: 응답의 콘텐츠 타입을 설정하는 메서드입니다.
    • 예시: 우유 병에 "딸기 우유" 라벨을 붙이는 것과 같아요.
  4. getRequestDispatcher: 다른 서블릿이나 JSP로 요청을 전달하는 메서드입니다.
    • 예시: 친구에게 부탁해서 다른 친구에게 메시지를 전달하는 것과 같아요.

 

Forward와 Redirect의 차이점과 예시

Forward

  • 기능: 서버가 클라이언트의 요청을 다른 서버 자원(JSP, 서블릿)으로 전달합니다. 클라이언트는 처음 요청한 URL이 바뀌지 않습니다.
  • 사용 상황: 내부에서 요청을 처리할 때 사용하며, 서버 내에서의 이동이므로 클라이언트는 이 과정을 모릅니다.
  • 예시: 도서관 사서에게 책을 찾는 방법을 물어보면, 사서가 자신이 가지고 있는 목록을 보여주는 것과 같아요.
request.getRequestDispatcher("/newPage.jsp").forward(request, response);

Redirect

  • 기능: 클라이언트에게 새로운 URL로 이동하라고 지시합니다. 클라이언트가 새로운 URL로 다시 요청을 보냅니다.
  • 사용 상황: 클라이언트의 브라우저가 새로운 URL을 직접 요청하게 할 때 사용합니다.
  • 예시: 도서관 사서에게 책을 찾는 방법을 물어보면, 사서가 "저쪽 건물로 가서 물어보세요"라고 안내하는 것과 같아요
response.sendRedirect("/newPage.jsp");

비교

  • Forward: 서버 내부 이동, 클라이언트는 URL 변화 없음, 요청과 응답 객체가 유지됨.
  • Redirect: 클라이언트에게 새로운 요청, 클라이언트는 새로운 URL 요청, 새로운 요청/응답 객체 생성.

시나리오 예시

Forward

// 현재 페이지에서 결과를 보여주는 페이지로 이동 (사용자 요청 URL 변경 없음)
request.setAttribute("message", "처리가 완료되었습니다.");
request.getRequestDispatcher("/result.jsp").forward(request, response);

Redirect

// 로그인 후 메인 페이지로 이동 (사용자 요청 URL 변경)
response.sendRedirect("/main.jsp");

이렇게 forward와 redirect는 각각 내부 처리와 외부 이동에 사용되며, 상황에 따라 적절히 선택하여 사용합니다.


getRequestDispatcher의 사용 상황과 예시

기능: getRequestDispatcher는 요청을 다른 서블릿이나 JSP로 전달하기 위해 사용됩니다. 이를 통해 서버 내에서 요청 처리를 다른 리소스로 넘길 수 있습니다.

사용 상황:

  1. 페이지 이동 없이 다른 리소스에 요청을 전달: 클라이언트에게 새로운 페이지로 이동시키지 않고, 서버 내부에서 요청을 다른 서블릿이나 JSP로 전달할 때 사용합니다.
  2. 재사용 가능한 컴포넌트: 헤더, 푸터, 사이드바 등 재사용 가능한 부분을 여러 페이지에서 사용할 때 유용합니다.
  3. 비즈니스 로직과 프레젠테이션 로직 분리: 비즈니스 로직을 처리한 후 결과를 JSP로 전달하여 프레젠테이션 로직을 처리할 수 있습니다.

예시

로그인 예시: 사용자가 로그인하면, 로그인 성공 여부에 따라 다른 페이지로 포워딩하는 경우입니다.

// 사용자가 로그인을 요청했을 때
String username = request.getParameter("username");
String password = request.getParameter("password");

// 로그인 로직 처리
boolean isAuthenticated = authenticateUser(username, password);

if (isAuthenticated) {
    // 로그인 성공 시, 메인 페이지로 포워딩
    request.getRequestDispatcher("/mainPage.jsp").forward(request, response);
} else {
    // 로그인 실패 시, 에러 페이지로 포워딩
    request.setAttribute("errorMessage", "Invalid username or password");
    request.getRequestDispatcher("/loginError.jsp").forward(request, response);
}

요약

getRequestDispatcher는 서버 내에서 요청을 다른 서블릿이나 JSP로 전달할 때 사용됩니다. 이는 페이지 이동 없이 서버 측에서 요청을 처리하고 응답을 생성할 때 유용합니다.

 

getRequestDispatcher는 요청을 다른 리소스로 전달하기 위한 객체를 반환하는 메서드이고, forward는 그 객체를 사용하여 실제로 요청을 전달하는 메서드입니다.

getRequestDispatcher

기능: 특정 URL 경로로의 요청을 처리하는 RequestDispatcher 객체를 반환합니다.

사용 방법: RequestDispatcher 객체를 얻기 위해 사용합니다.

RequestDispatcher dispatcher = request.getRequestDispatcher("/destination.jsp");

 

기능: 클라이언트에게 응답을 보내지 않고, 서버 내부에서 다른 서블릿이나 JSP로 요청을 전달합니다. 사용 방법: RequestDispatcher 객체를 통해 요청을 실제로 전달합니다.

dispatcher.forward(request, response);

사용 예시

RequestDispatcher dispatcher = request.getRequestDispatcher("/destination.jsp"); 
dispatcher.forward(request, response);

요약

  • getRequestDispatcher: RequestDispatcher 객체를 반환합니다.
  • forward: RequestDispatcher 객체를 사용해 요청을 다른 리소스로 전달합니다.

이 둘을 함께 사용하여 요청을 서버 내부에서 다른 리소스로 전달합니다.

 

 

RequestDispatcher

기능: RequestDispatcher는 웹 애플리케이션에서 요청을 다른 서블릿, JSP, 또는 HTML 파일로 전달하거나 포워드하는 데 사용되는 객체입니다. 클라이언트가 요청한 내용을 서버 내부의 다른 리소스에서 처리하도록 할 때 유용합니다.

주요 메서드

  1. forward: 요청을 다른 리소스로 전달하여 클라이언트가 요청한 내용을 처리하고 응답을 생성합니다.
  2. include: 현재의 응답에 다른 리소스의 출력을 포함시킵니다.

 

사용 예시

포워딩:

RequestDispatcher dispatcher = request.getRequestDispatcher("/anotherServlet");
dispatcher.forward(request, response);

이 코드는 /anotherServlet으로 요청을 전달합니다. 클라이언트는 URL이 바뀌지 않으며, 서버 내부에서 요청이 처리됩니다.

 

인클루드:

RequestDispatcher dispatcher = request.getRequestDispatcher("/header.jsp"); 
dispatcher.include(request, response);

이 코드는 header.jsp의 내용을 현재 응답에 포함시킵니다.

요약

RequestDispatcher는 요청을 다른 리소스로 전달하거나 현재 응답에 포함시키는 기능을 제공하여, 서버 내부에서 요청을 유연하게 처리할 수 있게 해줍니다.

 


내가 맡은 부분

 

* 회원 관리 - 삭제

* 로그아웃

* 실시간 웹 오목 채팅

* 코드리팩토링

 

1. 회원 관리 삭제

UserDAO.java

// 회원 삭제
public boolean delMember(String paramId, String paramPwd) {
    Connection con = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null; // ResultSet 객체는 쿼리 결과를 담기 위해 사용되지만, 이 메서드에서는 필요하지 않습니다.
    boolean result = false; // 삭제 결과를 저장할 boolean 변수, 초기값은 false로 설정

    try {
        // 데이터베이스 연결
        con = dataSource.getConnection();
        
        // 삭제 쿼리 준비
        String query = "delete from user where user_name = ? and user_pw = ?";
        preparedStatement = con.prepareStatement(query);
        preparedStatement.setString(1, paramId); // 첫 번째 물음표에 사용자 ID 설정
        preparedStatement.setString(2, paramPwd); // 두 번째 물음표에 사용자 비밀번호 설정
        
        // 쿼리 실행 및 영향받은 행 수 확인
        int row = preparedStatement.executeUpdate();
        if (row == 0) {
            result = false; // 삭제된 행이 없으면 false
        } else {
            result = true; // 삭제된 행이 있으면 true
        }
    } catch (Exception e) {
        e.printStackTrace(); // 예외 발생 시 스택 트레이스 출력
    } finally {
        // 리소스 해제 (ResultSet은 사용되지 않으므로 불필요함)
        try {
            if (resultSet != null) resultSet.close();
        } catch (Exception e) {
            // ResultSet이 null이면 close() 호출이 필요하지 않음
        }
        try {
            if (preparedStatement != null) preparedStatement.close();
        } catch (Exception e) {
            // PreparedStatement가 null이면 close() 호출이 필요하지 않음
        }
        try {
            if (con != null) con.close();
        } catch (Exception e) {
            // Connection이 null이면 close() 호출이 필요하지 않음
        }
    }
    return result; // 삭제 결과 반환
}

 

  1. 변수 선언 및 초기화:
    • Connection con: 데이터베이스 연결 객체.
    • PreparedStatement preparedStatement: SQL 쿼리를 실행하기 위한 객체.
    • ResultSet resultSet: 쿼리 결과를 저장하기 위한 객체 (하지만 이 메서드에서는 사용되지 않음).
    • boolean result: 삭제 성공 여부를 저장할 변수, 초기값은 false.
  2. try 블록:
    • 데이터베이스 연결을 가져옵니다.
    • 삭제할 사용자 정보를 기반으로 쿼리를 준비합니다. 쿼리는 사용자 이름과 비밀번호가 일치하는 행을 삭제합니다.
    • 쿼리를 실행하고, 영향받은 행 수를 반환받습니다.
    • 영향받은 행 수가 0이면 result는 false로 유지되고, 1 이상이면 result를 true로 설정합니다.
  3. catch 블록:
    • 예외가 발생하면 스택 트레이스를 출력합니다.
  4. finally 블록:
    • 사용된 리소스를 해제합니다. ResultSet, PreparedStatement, Connection 객체를 각각 닫습니다.
    • 각각의 객체를 닫을 때, 객체가 null인지 확인하고, null이 아닐 때만 닫습니다.
  5. 반환:
    • 삭제 결과를 boolean 값으로 반환합니다. 삭제가 성공했으면 true, 실패했으면 false를 반환합니다.

 

UserServlet.java

// 회원 삭제 로직 처리
} else if ("delmember".equals(cmd)) {
    UserDAO dao = new UserDAO(); // DAO 객체 생성
    HttpSession session = request.getSession(); // 현재 세션 가져오기
    String userName = (String) session.getAttribute("name"); // 세션에서 사용자 이름 가져오기

    // 삭제 요청 로그 출력
    System.out.println("cmd: " + cmd);
    System.out.println("id: " + id);
    System.out.println("pwd: " + pwd);

    // DAO를 사용하여 사용자 삭제 시도
    boolean result = dao.delMember(userName, pwd);
    if (result) {
        session.invalidate(); // 세션 무효화
        request.setAttribute("msg", "회원이 삭제되었습니다.");
        request.setAttribute("url", "/landing");
        request.getRequestDispatcher("/WEB-INF/view/include/alert.jsp").forward(request, response);
    } else {
        request.setAttribute("msg", "비밀번호를 틀렸습니다. 회원 삭제에 실패했습니다.");
        request.setAttribute("url", "/main");
        request.getRequestDispatcher("/WEB-INF/view/include/alert.jsp").forward(request, response);
    }
}

상세 설명

  1. UserDAO 인스턴스 생성: 데이터베이스 작업을 담당하는 DAO(Data Access Object) 클래스의 인스턴스를 생성합니다.
  2. 세션 관리: 사용자의 세션을 가져와 사용자 이름 정보를 추출합니다.
  3. 로그 출력: 처리 과정 중에 명령어(cmd), 사용자 ID(id), 비밀번호(pwd)를 콘솔에 출력합니다.
  4. 회원 삭제 실행: delMember 메서드를 호출하여 사용자 이름과 비밀번호로 회원 삭제를 시도하고 결과를 boolean 값으로 받습니다.
  5. 결과 처리:
    • 성공 시: 세션을 무효화하고, 사용자에게 삭제 성공 메시지와 함께 리다이렉트할 페이지(/landing)를 설정한 후, 경고창을 표시하는 JSP로 이동합니다.
    • 실패 시: 사용자에게 실패 메시지와 함께 메인 페이지(/main)로 리다이렉트할 URL을 설정한 후, 경고창을 표시하는 JSP로 이동합니다.

이 코드는 MVC 패턴에서 컨트롤러의 역할을 수행하여, 모델과 뷰 사이의 상호작용을 조정하고 사용자 요청에 따라 적절한 뷰를 선택하여 응답을 제공합니다.

 


2. 실시간 채팅

WebSocket.java

// WebSocket 객체 생성 및 이벤트 핸들러 설정
webSocket = new WebSocket("ws://192.168.0.166:9090/" + room + "/" + type);
webSocket.onopen = function (e) {
    appendChatMessage({message: "방에 입장 하였습니다.", type}, type);
    sendMessage({enemyName: name, event: "naming"});
};
webSocket.onmessage = function (e) {
    let obj = JSON.parse(e.data);
    if (obj.event == "chat") {
        appendChatMessage(obj, type);
    }
};

// 메시지 보내기
function sendMessage(obj) {
    webSocket.send(JSON.stringify(obj));
}

// 채팅 메시지 화면에 추가
function appendChatMessage(obj, type) {
    let chatmain = document.querySelector("#chatmain");
    if (obj.type == type) {
        chatmain.insertAdjacentHTML('beforeend', "<div class='chatmain-right-container'><div class='chatmain-right'>" + obj.message + "</div></div>");
    } else {
        chatmain.insertAdjacentHTML('beforeend', '<div class="chatmain-left-container"><div class="chatmain-left">' + obj.message + '</div></div>');
    }
    let scroll = document.querySelector("#chatmain");
    scroll.scrollTop = scroll.scrollHeight;
}

// 메시지 전송 버튼 이벤트 리스너
document.querySelector("#msgbutton").addEventListener('click', function () {
    sendMessage({message: document.querySelector("#msgtext").value, event: 'chat'});
    document.querySelector("#msgtext").value = '';
});

 

기능 설명

  1. WebSocket 연결: 서버의 주소와 함께 WebSocket 연결을 초기화하고, 이벤트 핸들러를 설정합니다. 연결이 성공하면 방에 입장했다는 메시지를 보냅니다.
  2. 메시지 수신 처리: 서버로부터 메시지를 받으면 JSON 형식으로 파싱 후, 채팅 이벤트인 경우 채팅 메시지를 화면에 추가합니다.
  3. 메시지 전송 함수: 메시지 객체를 JSON 문자열로 변환하여 WebSocket을 통해 서버로 전송합니다.
  4. 채팅 메시지 추가: 사용자가 보낸 메시지를 적절한 위치에 HTML로 추가하고, 채팅창을 자동으로 스크롤하여 최신 메시지를 보여줍니다.
  5. 전송 버튼 이벤트 리스너: 메시지 전송 버튼에 클릭 이벤트 리스너를 등록하여 입력된 메시지를 전송하고 입력 필드를 초기화합니다.

3. 로그아웃

LogoutServlet.java

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

// Servlet 선언 및 URL 매핑
@WebServlet(name = "logoutServlet", value = "/logout")
public class LogoutServlet extends HttpServlet {

    // GET 요청 처리
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doHandle(request, response);
    }

    // POST 요청 처리
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doHandle(request, response);
    }

    // 로그아웃 처리 공통 로직
    protected void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession(false); // 현재 세션 가져오기, 세션이 없다면 null 반환

        if (session != null) {
            System.out.println("logout"); // 콘솔에 로그아웃 로그 출력
            session.invalidate(); // 세션 무효화
        }
        response.sendRedirect("/landing"); // 로그아웃 후 landing 페이지로 리다이렉션
        // response.sendRedirect(request.getContextPath() + "/landing"); // 컨텍스트 경로를 포함한 리다이렉션
    }
}

상세 설명

  1. @WebServlet: 이 어노테이션은 서블릿을 선언하고, /logout URL로 매핑합니다. 이 URL로 들어오는 요청은 이 서블릿에서 처리됩니다.
  2. doGet과 doPost 메소드: 모든 종류의 HTTP 요청(GET, POST)을 처리하기 위해 doHandle 메소드를 호출합니다.
  3. doHandle 메소드: 로그아웃 로직을 중앙에서 관리합니다. 세션을 확인하고 있으면 로그아웃 처리를 하고 세션을 무효화합니다. 그 후, 사용자를 로그아웃 상태를 알리는 페이지(예: landing 페이지)로 리다이렉트합니다.

이렇게 중앙 처리 메소드를 사용함으로써 코드의 중복을 줄이고, 유지보리성을 향상시키는 효과가 있습니다.


4. 코드 리팩토링
 
CustomGamePage.jsp
// WebSocket을 이용한 실시간 게임 통신 초기화
var webSocket;
// 게임 상태 및 플레이어 속성을 추적하기 위한 변수 정의
var currentUser, myStoneColor, enemyStoneColor;
var type = "<%=session.getAttribute('type')%>";
var room = "<%=session.getAttribute('room')%>";
var name = "<%=session.getAttribute('name')%>";

// 플레이어 타입에 따라 변수 설정
if (type == "create") {
    currentUser = "O";
    myStoneColor = "black";
    enemyStoneColor = "white";
} else {
    currentUser = "X";
    myStoneColor = "white";
    enemyStoneColor = "black";
}

// 바둑알 색상 설정 함수
function setStoneColor(stoneColor, elementId) {
    document.querySelector(elementId).src = '/img/' + stoneColor + 'dot.png';
}

// 채팅 메시지 추가 함수
function appendChatMessage(obj, type) {
    let chatmain = document.querySelector("#chatmain");
    if (obj.type == type) {
        chatmain.insertAdjacentHTML('beforeend', "<div class='chatmain-right-container'><div class='chatmain-right'>" + obj.message + "</div></div>");
    } else {
        chatmain.insertAdjacentHTML('beforeend', '<div class="chatmain-left-container"><div class="chatmain-left">' + obj.message + '</div></div>');
    }
    let scroll = document.querySelector("#chatmain");
    scroll.scrollTop = scroll.scrollHeight;
}

// WebSocket을 통해 메시지 전송 함수
function sendMessage(obj) {
    webSocket.send(JSON.stringify(obj));
}

// 바둑알 위치 설정 함수
function placeStone(x, y, stoneColor) {
    const go = document.getElementById('go');
    const stone = document.createElement('img');
    stone.src = '/img/' + stoneColor + 'dot.png';
    stone.className = 'stone';
    stone.style.position = 'absolute';
    var stoneX = (x * 53 + 34) - (stone.width / 2);
    var stoneY = (y * 53 + 34) - (stone.height / 2);
    stone.style.left = stoneX + 'px';
    stone.style.top = stoneY + 'px';
    stone.style.zIndex = '6';
    go.appendChild(stone);
}

// 경고 메시지 표시 함수
function showAlert(alertTitle, alertText, alertIcon, alertConfirmButtonText) {
    Swal.fire({
        title: alertTitle,
        text: alertText,
        icon: alertIcon,
        confirmButtonText: alertConfirmButtonText
    });
}
 

각 기능의 상세 설명

  • WebSocket 통신 초기화: 실시간 게임 상호작용을 위해 서버와의 WebSocket 연결을 설정합니다.
  • 플레이어 속성 설정: 게임의 시작 유형에 따라 플레이어의 현재 사용자 표시자(currentUser)와 바둑알 색상(myStoneColor, enemyStoneColor)을 설정합니다.
  • 바둑알 색상 설정: 특정 HTML 요소의 이미지 소스를 변경하여 바둑알의 색상을 업데이트합니다.
  • 채팅 메시지 추가: 게임 채팅 영역에 새 메시지를 추가하고 자동으로 스크롤하여 최신 메시지를 표시합니다.
  • 메시지 전송: 사용자 입력을 JSON 형식으로 서버에 전송합니다.
  • 바둑알 배치: 사용자가 클릭한 위치에 바둑알을 배치하고 해당 위치를 계산하여 스타일을 적용합니다.
  • 경고 메시지 표시: SweetAlert 라이브러리를 사용하여 사용자에게 다양한 상황에 대한 알림을 제공합니다.

이 코드는 게임의 실시간 상호작용, 사용자 피드백, 그리고 게임 로직의 관리를 위해 정교하게 설계되었습니다.