프로젝트/메타버스 게임

[메타버스 게임] 캐쥬얼 배틀로얄 프로젝트 계층형 아키텍쳐 구현

마달랭 2025. 4. 1. 14:04

개요

빌드 환경을 구현하였으니 본격적인 서버 구현에 앞서 구현하고자 하는 아키텍쳐를 구상해봤다.

내가 개발해야할 서버에선 클라이언트 세션을 관리하고 클라이언트 요청에 따라 인증, 매칭등의 서비스 로직을 구현해야 한다. 이를 데이터베이스와 통신하여 트랜잭션 처리를 통해 검증 및 상태 업데이트를 진행해 주어야 한다.

 

따라서 controller를 통해 클라이언트 요청을 구분하여 각 기능에 맞는 API를 호출할 수 있도록 서비스를 핸들해 주기로 했다. 예를 들어 인증 관련은 auth, 매칭 관련은 room, 게임 생명 주기 관리는 game의 컨트롤러 타입을 지정하고, 컨트롤러 핸들러를 통해 각 API가 호출될 수 있도록 나누어줬다.

 

처음엔 패킷을 주고 받을 때 최대한 적은 리소스를 사용하고자했지만 게임 내 이벤트에 해당하는 로그처리에 대한 기능이 스펙 아웃되면서 편하게 Json타입을 활용하여 클라이언트와 통신하도록 약속하였다.

하기에 어떤식으로 계층을 분리하여 아키텍쳐를 구현하였는지 설명해 보겠다.

 

 

main

서버 구동의 시작점이다, spdlog를 사용해 서버 실행 시 콘솔에 표시될 내용을 초기화 해준다, 또한 서버 포트를 지정하고 db연결 정보를 지정해준다. 실제 배포 환경에선 해당 내용은 모두 환경 변수로 넘겨주어야 한다.

해당 내용을 바탕으로 io_context와 함께 Server에 인자로 넘겨주어 서버를 시작해 준다.

 

 

core

server와 session에 대한 로직이 담겨있는 계층이다.

서버가 생성되면 db커넥션의 개수를 지정하여 데이터베이스 풀을 초기화 하고, 컨트롤러 및 레포지토리, 서비스를 초기화 해준다. 이후 run상태로 돌입해 리스닝 상태에서 클라이언트 요청을 받아준다.

 

session에선 클라이언트의 접속 요청이 서버에서 accept되었을 경우 부터 생성이 된다.

session이 생성되면 컨트롤러에 대한 정보를 전달 받고, 고유한 토큰을 서버로부터 전달 받는다, 이는 서버측의 해시맵을 통해 토큰 정보가 저장된다. 이 때 부터 서버와 세션은 서로 통신할 준비를 마쳤으며 주로 클라이언트가 서버측으로 요청을 보내면 서버에서 요청에 대한 처리 후 응답을 보내주는 통신을 하게 된다.

 

session이 종료되었을 경우 클라이언트에게 할당된 자원을 모두 회수하고 서버측에서 세션에 대한 정보를 지워 유효하지 않는 세션에 대한 참조를 막는다.



controller

클라이언트 요청을 받아 핸들러를 통해 어떤 API에 배정되어야 할지 지정해주는 역할을 하는 계층이다.

세션에서 클라이언트로 부터 요청이 전달 되었을 경우 약속된 타입 action을 통해 컨트롤러 타입을 지정하여 핸들러로 보내주게 된다. 핸들러에선 각 타입에 맞는 컨트롤러로 배정하여 action타입에 따라 서비스 로직에 API호출을 진행하게 된다.

 

이 때 유효하지 않은 action타입일 경우 컨트롤러에게 전달되기 전에 세션 계층에서 예외 처리를 해주어야 하며, request에 포함되어야 하는 추가 정보가 있다면 추가 후 컨트롤러에 전달해 줘도 된다.

 

 

service

실제 비즈니스로직을 수행하는 역할을 하는 계층이다.

컨트롤러로 부터 배정받은 메서드를 실행하게 되며 각 메서드엔 DB와 통신하여 받은 데이터를 토대로 요청 성공여부를 반환하여 응답을 컨트롤러로 반환하게 된다. 이 서버는 CRUD트랜잭션을 주로 사용하므로 모든 서비스 계층에선 최소 1개 이상의 레포지토리를 사용하여 DB와 트랜잭션을 진행하게 된다.

 

주의할 점은 repository에 전달해 줘야 하는 매개변수 인자가 실제 클라이언트로 부터 전달 받은 request에 포함되어 있는지 체크를 해주어야 한다. 존재하지 않는 키를 가져오려고 했다가 서버가 뻗어버리는 일을 많이 겪었다.

 

 

repository

트랜잭션 처리를 통해 데이터베이스 CRUD를 수행하는 역할을 하는 계층이다.

서비스 계층으로부터 메서드 호출이 되면 DB와 통신하며 알맞은 트랜잭션 작업을 수행한다. 수행 중 에러가 발생할 경우 예외를 발생시켜야 한다. 에러 없이 트랜잭션이 모두 잘 수행되었을 경우 커밋 후 서비스 로직이 원하는 값을 리턴해 주면 된다.

 

주의할 점은 수행 중 에러가 발생하거나 검증 실패 시 예외를 반환할 때 트랜잭션을 abort처리해 주어야 한다, 당연히 코드블록이 종료되면 abort처리되고 사용된 db커넥션이 반환될 줄 알았지만 abort처리해 주지 않은 경우 트랜잭션에 사용된 db커넥션이 풀에서 사용 불가 상태로 처리되는 점을 확인하였다.



util

데이터베이스, 패스워드 해싱과 같은 유틸리티가 포함된 계층이다.

사실상 계층이라 보긴 어렵고 그냥 옵션이라고 보면 될 듯 하다, 데이터베이스 관련 로직은, 서버가 실행되었을 때 db풀을 n의 크기로 초기화 하는 과정과, db풀에서 사용할 수 있는 db커넥션을 찾기, 존재하지 않을 경우 새로운 db커넥션을 db풀에 추가해 주기 등에 대한 로직이 존재한다.

 

암호화 유틸리티의 경우 사용자가 회원가입을 할 때 입력한 패스워드를 DB에 직접 저장하지 않고, SHA-256 해싱 알고리즘을 통해 해싱한 값을 저장해 주기 위함이다. 이 외에도 추가 기능이 있는 소스 파일이 추가될 경우 util디렉토리에 위치해 주면 된다.

 

 

마치며

소켓 서버에서의 계층형 아키텍쳐를 대충이나마 알아보았다, 현재 프로젝트를 진행하며 계속 코드 수정이 이루어지고 있는 관계로 프로젝트 종료 시 관련 계층의 코드를 리뷰해 볼 생각이다.

728x90