Java 프로젝트 회고 (2024.04.17)

YJ·2024년 4월 17일
post-thumbnail

이번 시리즈의 마지막 포스트로
지금까지 배운 Java 개념들을 활용하여
일주일 동안 진행한
Java Swing 프로젝트를 회고하고자 합니다.

프로젝트 명: JJAPGUN
기간: 2024.4.11 ~ 2024.4.17
깃허브: https://github.com/PDA-JJAPGUN/JJAPGUN

맡은 역할

PM (Project Manager)

구현 기능

  • User
    • MVC 패턴 구현
    • UserView 형식 구현
    • 회원가입 로직
    • 회원가입 중복검사
    • 로그인 로직
    • 로그아웃 로직
    • 세션 로직
  • Player
    • 플레이어 비행기 화면에 띄우기
    • 플레이어 비행기 움직이게
    • 플레이어 비행기 총알 발사
    • 플레이어 죽음 && LIFE 감소 - 적 비행기 충돌 시
    • 총알 맞으면 적 비행기 죽게 하기
    • 플레이어 죽음 && LIFE 감소 - 적 비행기 총알 맞을 시
    • 플레이어 목숨 줄어들 때 목숨, 점수 가운데 정렬되는 버그 수정
    • 보스 죽음
    • 점수 데이터 연결
  • Refactoring
    • MainController 생성
    • GameController에서 유저 가져오는거 UserController로 분리함으로써 Ranking 아키텍처 구조 수정
    • UserView에 nickname이 출력되도록 수정

User 구현 기능

User 클래스 다이어그램

User와 관련된 클래스와 인터페이스는 다음과 같습니다.

UserView
: User 기능만 담당하는 View Class

UserController
: User 기능만 담당하는 Controller

UserService
: User 기능의 비즈니스 로직만 처리하는 Service

UserDAO
: User 기능의 데이터만 처리하는 인터페이스

UserDAOImpl
: UserDAO 인터페이스를 구현한 클래스로 User 기능의 데이터만 처리하는 Class

LoginDto
: 로그인과 관련된 데이터만 저장하는 클래스

SignupDto
: 회원가입과 관련된 데이터만 저장하는 클래스

UserEntity
: 유저에 관한 데이터만 저장하는 클래스

UserSession
: 로그인, 로그아웃 상태 정보를 저장하기 위한 클래스

회원가입

회원가입은 다음과 같은 로직으로 설계하였습니다.

  1. UserView에서 id, password, nickname을 입력합니다.
  2. UserView에서 SignupDto 클래스의 생성자로 id, password, nickname를 전달하여 객체를 UserController로 전달합니다
  3. UserController에서 SignupDto를 UserService로 전달합니다.
  4. UserService에서 UserEntity 클래스의 필드인 bestScore 필드를 null 값을 할당하여 선언 한 후, 유저에 대한 회원 중복 검사를 진행한 후에, 중복된 User가 아닐 경우, Dto → Entity로 변환하여 싱글톤 인스턴스인 UserDAOImpl로 전달합니다.
  5. UserDAOImpl에서 store 필드에 UserEntity를 저장합니다.
  6. 다시 UserDAOImpl에서 UserView까지 회원가입 로직 처리에 대한 반환값을 전달합니다.
  7. 화면에서 회원 가입 처리에 대한 결과를 확인할 수 있습니다.

회원가입 성공시 화면

중복된 ID로 회원가입 실패시 화면

회원가입 주요 코드

UserService.java

회원가입에서 주요하게 볼 수 있는 코드는
UserService Class의 signup 메서드입니다.
UserService의 signup 메서드는 UserController에서 SignupDto를 전달받습니다.
그리고, 여기서 ID 중복 검사를 먼저 실시 합니다.

UserDAOImpl.java

위 코드는 회원가입시 ID 중복 검사를 하는 DAO 메서드입니다.
현재 DAO에는 유저 정보를 저장하는 store라는 변수는 아래와 같은 변수로 선언되어 있습니다.

위 코드 처럼 Map<String, UserEntity> 타입으로 선언되어 있고, Map의 key 값을 ID로 사용하도록 정의하였습니다.
따라서, 중복 ID 로직에서 현재 store의 모든 key 값 중 회원가입을 위해 View에서 받아온 ID가 포함되어있는지 여부에 따라서 중복 여부를 판단하여 boolean 값을 반환합니다.
중복되지 않을 경우에 SignupDto를 UserEntity로 변환합니다.
그리고 변환된 UserEntity를 UserDAOImpl에 전달하여 store 필드에 저장합니다.

로그인

로그인은 다음과 같은 로직으로 설계하였습니다.

  1. UserView에서 id, password을 입력합니다.
  2. UserView에서 LoginDto 클래스의 생성자로 id, password를 전달하여 객체를 UserController로 전달합니다
  3. UserController에서 LoginDto를 UserService로 전달합니다.
  4. UserService에서 사용자 인증 검사를 진행한 후에, 검사를 통과하면, Session에 유저에 대한 ID와 닉네임을 저장합니다.

로그인 주요 코드

UserService.java

로그인에서 주요하게 볼 수 있는 코드는
UserService Class의 login 메서드입니다.

login 메서드는 UserView에서 로그인 요청으로 ID와 Password를 LoginDto 객체로 변환하여 UserController에 전달하고, UserController는 UserService의 login 메서드에 이 LoginDto를 전달합니다.
UserService의 login 메서드는 전달받은 LoginDto에서 필드값인 ID와 Password를 조회한 후, 이 ID 값으로 UserDAOImpl의 store 변수에서 ID 값에 해당하는 User를 반환 받아 user 변수에 할당합니다.
그리고, 로그인 유효성 조건문에서 user 변수의 존재 유무와 user 변수의 비밀번호와 사용자로부터 입력받은 비밀번호가 일치하는지 확인합니다.
그 후 일치한다고 판단되는 유저의 경우
UserSession 싱글톤 인스턴스를 가져와 로그인한 유저의 ID와 Nickname을 할당하고, true를 UserController에 반환합니다.

로그아웃

로그아웃은 다음과 같은 로직으로 설계하였습니다.

  1. UserView에서 로그아웃 버튼을 누릅니다.
  2. Session에 저장되어 있는 유저에 대한 정보를 삭제합니다.

랭킹 (구조 Refactoring)

Refactoring 전

Refactoring 후

랭킹은 다음과 같은 로직으로 설계하였습니다.

  1. GameEnd 페이지에서 Rank 버튼을 클릭합니다.
  2. 요청이 GameEnd → GameTitle → GameRank View로 이동합니다.
  3. GameRank에서 UserController로 User 랭킹 정보 조회를 요청합니다.
  4. UserController에서 UserService로 User 랭킹 정보 조회를 요청합니다.
  5. UserService에서 DAO에서 모든 User에 대한 정보를 조회합니다. 그 후, User 정보를 bestScore에 순으로 정렬하여 GameRank에 반환합니다.

Refactoring 전에는 GameControllerUserDAOImpl에 접근하여 유저에 관한 모든 데이터를 조회한 후, GameController에서 유저의 bestScore를 정렬하여 GameRank View에 반환하는 로직이었습니다.

다시 구조를 Refactoring 하던 중, 저희가 생각하기에는 GameController는 Game에 관한 기능만 담당하는 것이 맞을 것 같다는 판단이 들었습니다. 따라서, 랭킹에 대한 기능은 GameController에서 UserController로 위임하는 것이 옳다는 판단이 들었고, 그렇기에 GameRank View에서의 랭킹 요청을 GameController -> UserController로 위임하였습니다.
또한, 기존에는 GameController에서 직접 DAO에 접근을 하고, GameController 내부 메서드에서 DAO에서 받아온 모든 유저의 정보를 bestScore로 정렬하여 GameRank에 리스트로 반환하였다면,
저희는 첫 번째로 DAO에 접근하는 것은 Service 계층에서만 담당할 것 이라는 생각과 두 번째로 User에 대한 비즈니스 로직 처리는 오직 UserService에서만 담당할 것 이라는 결정을 내리게 되었습니다.
이에 따라, UserService 클래스에서 모든 유저에 대한 정보를 UserDAOImpl에서 받아오고, bestScore에 대한 정렬 역시 UserService에서 담당하여 처리한 후 UserController로 반환하는 로직으로 수정하였습니다.
이러한 Refactoring을 통해 MVC 패턴을 고도화함으로써 세 가지 특징을 배운 것 같습니다.
각 객체는 자기가 해야할 기능만 담당해야 한다.
각 계층 역시 자신이 속한 계층이 해야할 기능만 담당해야 한다.
MVC 패턴을 적용하면 유지 보수하기에 편해진다.

총평 (좋았던 점, 아쉬웠던 점)

느낀점

Java와 Java Swing, MVC 패턴 등 프로젝트를 진행하기 위한 개념들을 배운 후 진행한 프로젝트였다. 서비스 기획부터 매일 진행되는 스프린트, 개발, 최종 구현까지 일주일이라는 시간 동안 열심히 진행 해본 것 같다. 화면을 구성하는 방법으로 React가 아닌 Java Swing을 사용하는 것이 처음이여서, 맨땅에 헤딩을 하는 느낌이기는 하였다.

팀원들과는 프론트엔드, 백엔드를 나누기 보다는 각 기능 별로 포지션을 잡고 진행을 하였다. 나와 진현님은 회원관리를 위한 User와 관련된 부분과 게임 내에서 플레이어를 구현하기 위해 Player, PlayerAttack 부분을 담당하여 개발을 진행하였다.

짧은 시간이라면 짧고 긴 시간이라면 긴 일주일 동안
수업 시간에 배운 개념들을 최대한 녹아내어 프로젝트에 적용해보려고 하였고, 구현까지 마무리하게 되어 프로젝트 마지막 날에는 성취감이 많이 느껴졌다.
객체지향의 특징, MVC 패턴, Java Swing 등 배운 개념들을 빠르게 프로젝트에 녹여내기 위하여 수업시간에 배운 내용들을 정리해 놓은 나의 블로그를 통해 다시 복습하였고, 유튜브 등 여러 자료들을 통해 MVC 패턴, Java Swing에 대해 공부하였다. 최대한 바로 개발로 뛰어들려고 나름의 노력을 한 것 같다.

좋았던 점

첫 번째는, MVC 패턴에 조금이나마 가까워진 느낌이었다.
User를 MVC 패턴으로 관리해보면서 DTO, DAO, Entity에 대한 개념에 대해 배우고 적용하게 되어 MVC에 한 단계 가까워진 느낌이었다.
강사님께 이 로직에 대한 질문을 드렸고, 그에 대한 답변으로 이 개념들이 후에 Spring에서 다뤄질 개념들이라는 것을 말씀해주셨다.
현재는 DAO 인터페이스를 상속한 DAO 싱글톤 구현 클래스에서 데이터를 저장하도록 클래스를 설계하였지만, Spring을 배우고 나서는 RDB를 주로 사용할 것이고 이를 다루기 위해 JPA를 구현한 Repository 클래스를 통해 DB에 접근을 하는 것을 알게 되었다. 지금은 View - Controller - Service - DAO 구조로 Model을 Service와 DAO로 구분 하는 방식으로 설계하였지만, Spring에서는 View - Controller - Service - Repository - DB 순으로 설계하여 프로젝트를 리팩토링 할 것이다. 그리고, 지금은 DAO 클래스에서 저장할 데이터를 UserEntity 클래스의 객체들을 저장하도록 설계하였지만, 나중에는 RDB의 필드 값들과 유사한 Entity를 설계하고, 이 Entity를 JPA 인터페이스를 사용하여 RDB에 CRUD를 구현하도록 리팩토링 할 것이다. 이번 프로젝트를 통해 MVC 패턴이 무엇인지 조금은 감을 잡게 될 수 있었고, 후에 진행할 프로젝트에서 이 패턴으로 여러 기능들을 관리해보고 싶다는 열망이 생긴 것 같다.

두 번째는, 객체 지향적인 사고를 많이 해볼 수 있어서 좋았다.
세부 클래스들을 먼저 정의해보고, 정의한 클래스들에서 공통되는 규약들은 상위 추상 클래스나 인터페이스에서 선언하는 과정들을 통해, 상속, 다형성 등 객체 지향의 특징들을 적용해보고 왜 이렇게 설계하는 것이 중요한지 몸소 느껴본 것 같다.
하나의 클래스가 여러 기능들을 담당하는 것은 유지보수 부분에서 어려움이 많이 발생할 것이라는 걸 느끼게 되었다. 이번에 진행한 프로젝트에서도 Player 클래스를 정의하면서 Player 클래스 안에 여러 기능들을 Player가 담당하도록 하다 보니 코드가 복잡해지고, 코드의 규모도 매우 커지는 것을 경험하였다. 처음에는 Player 안에 플레이어가 공격하는 기능, 총알과 관련된 기능들을 함께 구현을 하였더니 Player 객체가 담당하는 부분이 너무 많아졌고, 개발을 진행하면서 Enemy, Boss 등 다른 클래스와의 의존성이 높아졌다. 이러한 의존성을 최대한 줄이고, 하나의 클래스가 오직 본인의 기능 만을 담당하도록 하기 위해 Player, PlayerAttack, Bullet과 같은 클래스로 분리함으로써 각 클래스는 오직 본인의 기능만을 담당하도록 수정하였고, 이를 통해 각 클래스별로 의존성은 줄이고 독립성을 높이는 경험을 해보았다. 이 경험을 거름삼아 금융 프로젝트를 진행하면서 개발할 많은 기능들을 객체 지향스럽게 개발해보고 싶다.

세 번째는, 좋은 팀원들과 함께 팀 프로젝트를 진행할 수 있어서 좋았다. Project Manager 업무를 맡으면서, 처음에는 어떻게 프로젝트를 진행하도록 해야 할지 난감하였다. 그러나, 팀원 분들이 각자의 위치에서 최선을 다해 프로젝트의 완성을 향해 달려주어 완성도 높은 프로젝트를 진행할 수 있었다. 모두들 정말 고생하였습니다!

아쉬웠던 점

첫 번째는, MVC 패턴으로 Game 관련 로직들을 설계하지 못한 것이었다. 기획에 많은 시간을 투자하여 개발에 시간을 부족하게 잡게 되었고, 그렇기에 User에 대한 로직만 MVC로 처리하게 되었다. 앞으로 JJAPGUN 프로젝트를 Spring을 적용하여 리팩토링하게 된다면 Game 관련 로직들도 MVC로 설계하여 각 기능 별로 View, Controller, Service, DB 등으로 나누어서 개발해보고 싶다.

두 번째는,객체 지향적으로 구현을 하도록 노력하였으나 아직 추상화에 많은 시간을 투자하지 못한 것 같다. Game 로직에서 Player 기능에 공통되는 로직들을 더 상위 인터페이스로 만들어 관리해보고 싶고, User 부분에서 로그인 유효성 검증, 회원가입시 중복 ID 검증 등보안적인 로직 역시 상위 인터페이스로 관리해보고 싶다.

profile
dev

0개의 댓글