AnimalWar 프로젝트
👨🏫 프로젝트 요약
AnimalWar는 MSA(마이크로서비스 아키텍쳐)로 구성된 전략과 RPG 요소를 결합한 웹 기반 게임입니다.
프로젝트 설명은 Readme 참조
https://github.com/AnimalWarProject
AnimalWarProject
AnimalWarProject has 15 repositories available. Follow their code on GitHub.
github.com
🎨 ERD

프로젝트 초기 기획 단계에서 각 도메인의 데이터 구조와 관계를 명확히 파악하고, 도메인 주도 설계를 기반으로 마이크로서비스 간 역할을 명확히 나누기 위해 ERD를 직접 설계하였습니다. 주요 도메인은 User, Animal, Building, Market으로 구성되며, 각각은 실시간 게임 기능과 연동되어 서비스 단위로 분리되었습니다. User는 자원 및 보유 데이터를 관리하고, Animal과 Building은 속성과 능력치를 정의하여 Merge 시스템에 활용됩니다. Market은 유저 간 거래를 처리하기 위한 구조로, Kafka 기반 메시지 처리에 대응됩니다.
해당 ERD를 기반으로 각 마이크로서비스의 데이터베이스 스키마를 분리하고, UUID, Animal_id, Building_id 등을 기준으로 Kafka 메시지를 통한 비동기 연동 구조를 설계했습니다. 이는 이후 API 설계와 DTO 구조 도출의 기반이 되었으며, 팀원들과의 데이터 모델 협업 과정에서도 중요한 참조 기준으로 활용되었습니다.
📃 프로젝트 구조(아키텍쳐)

AnimalWar 프로젝트는 실시간 게임 기능, 유저 조작, 거래소, 조합 등의 복합적인 도메인을 포함하고 있었기 때문에, 초기부터 기능 확장성과 서비스 간 책임 분리를 고려하여 MSA 기반으로 설계하였습니다. 프로젝트를 워낙 거대하게 설계하여 단일 서비스 구조로는 유지보수 및 확장에 한계가 있다고 판단했고, 이에 따라 도메인 주도 설계를 기반으로 각 기능을 독립적인 마이크로서비스로 분리하였습니다.
전체 시스템은 4개의 계층(Client, Gateway, Service, Infra)으로 구성되어 있으며, React 기반의 SPA가 사용자 인터페이스를 담당하고, Spring Cloud Gateway가 인증과 라우팅을 수행합니다. 핵심 비즈니스 로직은 User, Mix, Draw 등 도메인 단위로 나뉜 마이크로서비스에 배치되었고, 이들은 Eureka와 Config Server를 통해 관리됩니다. 또한 서비스 간 통신은 Kafka 기반 비동기 메시징을 활용해 결합도를 낮추고, MySQL을 각 서비스에 맞춰 분산 구성하여 데이터 독립성과 확장성을 확보하였습니다.
MSA 설계 경험을 통해 실제 운영에 가까운 구조를 구현해보며, 서비스 간 데이터 흐름, 메시지 기반 통신, 설정 및 인프라 관리까지 전반적인 백엔드 시스템의 이해도를 높일 수 있었고, 확장성과 유연성을 갖춘 구조를 직접 설계하고 구축한 점에서 많은 성장을 느낄 수 있었습니다.
🎈 데이터 흐름도

① 데이터 입력
사용자는 React 기반의 프론트엔드를 통해 게임 내 행동을 수행합니다. 예를 들어 유닛을 조합하거나, 건물을 업그레이드하거나, 거래소에 아이템을 등록하는 등의 요청이 발생합니다. 해당 요청은 API 호출을 통해 백엔드로 전달됩니다.
② 전처리
모든 요청은 Spring Cloud Gateway를 거쳐 해당 마이크로서비스의 Controller에 도달하며, 요청에 포함된 JWT 토큰은 Gateway 또는 마이크로서비스 내에서 인증 필터를 통해 검증됩니다.
인증이 완료되면 Controller는 파라미터를 파싱하고, 유저 정보를 바탕으로 요청을 분기하거나 유효성 체크를 수행합니다.
③ 부분처리
Controller를 통해 전달된 요청은 Service 계층에서 처리되며, 실제 비즈니스 로직이 이곳에 구현되어 있습니다. 예를 들어 Mix 요청은 유닛의 속성과 보유 자원을 체크한 후, Kafka 메시지를 발행하여 별도의 Mix Service에서 결과를 처리하게 됩니다. 서비스 간 통신은 Kafka 기반 비동기 메시지로 이루어집니다.
④ DB 처리 (DAO & MySQL)
Kafka 메시지를 수신한 대상 서비스(예: Mix, Draw, Inventory 등)는 내부에서 비즈니스 처리를 마친 후, Spring Data JPA 기반 Repository를 통해 MySQL DB에 데이터를 저장하거나 조회합니다. 각 마이크로서비스는 독립된 DB 스키마를 보유하고 있으며, UUID, Animal_id, Building_id 등의 키를 기준으로 연관 데이터를 다루도록 설계되어 있습니다.
🍭 Kafka 흐름도

AnimalWar 프로젝트에서는 마이크로서비스 간의 효율적인 비동기 통신을 위해 Kafka를 도입하였습니다.
Kafka 기반의 이벤트 드리븐 구조는 서비스 간 의존도를 낮추고 확장성 높은 통신 구조를 가능하게 해주었습니다.
*이벤트 드리븐 : 시스템의 한 부분에서 어떤 일이 발생하면(이벤트 발생), 그걸 다른 서비스가 구독해서(이벤트 수신) 필요한 일을 처리하는 방식
① INPUT (React Frontend)
사용자가 게임 화면에서 동물 믹스 요청을 입력하면, 해당 요청이 API 호출을 통해 서버로 전달됩니다.
② USER SERVICE
컨트롤러에서 JWT 인증을 수행한 후, Kafka의 mix-topic 토픽으로 믹스 요청 메시지를 발행합니다
③ Kafka BROKER
Kafka는 이 메시지를 중계하고, 이를 구독하고 있는 Mix Service에게 전달합니다.
④ MIX SERVICE
메시지를 수신한 후 동물 믹스 로직을 실행하고, 결과를 다시 UserService 또는 InventoryService로 전달합니다.
⑤ 결과 반영
믹스 결과에 따라 유저의 인벤토리 정보를 갱신하며, 강화 결과, 보유 수량 등의 데이터가 업데이트됩니다.
🕵️♀️ 맡은 파트 (팀장)
① 기획안 제작 (기획 99%이상 기여)
② Figma 제작
③ 동물, 건물 DB 작성
④ UserService, TerrainService, CommunityService
⑤ Eureka, Config, Gateway 설정
⑥ 사이트 기본틀 제작, pixi 기본틀 제작
⑦ Spine 3d Animation
⑧ Security 설정
⑨ 건물 이미지 제작, 쥐 이미지 제작
👨🦲 좋았던 점
AnimalWar 프로젝트에서 팀장으로서 프로젝트 전반을 주도하며 많은 성장을 할 수 있었습니다. 가장 큰 보람은 단순히 개발만이 아닌, 기획, 커뮤니케이션, 문제 해결, 리딩 능력까지 폭넓은 역할을 수행했다는 점입니다.
우선 프로젝트 초기 기획 단계부터 직접 도메인과 기능을 정의하고, 전체 시스템의 흐름을 구성하는 기획안을 99% 이상 단독으로 제작하였습니다. 단순히 아이디어에 그치지 않고, ERD 및 전체 구조도를 직접 구상하며 팀원들과 구체적인 방향을 공유했고, 기능 분배 및 우선순위 설정, 일정 관리까지 총괄했습니다. 특히 핵심 기능이 몰려 있던 UserService의 설계 및 구현을 직접 담당하여, 인증/회원가입/자원/보유정보/랭킹 등 여러 기능을 정리하고 안정적으로 동작하도록 만들었습니다.
또한 팀장으로서의 역할에도 최선을 다했습니다. Slack 채널을 직접 개설하고 매일 진행 상황을 정리해 팀원들과 공유하였으며, 오전에는 등원하지 못한 팀원들과도 연락을 취해 진행 상황을 조율하고, 오후 5시에는 정기적인 코드 리뷰 및 피드백 회의를 통해 프로젝트 방향을 지속적으로 보완했습니다. 서비스 별로 필요한 DB나 통신 구조를 빠르게 도출해 백엔드와 프론트의 연계를 이끌었고, Pixi.js 기반 게임 연동을 위한 기본 틀도 직접 제작했습니다.
특히 좋은 경험이었던 점은, 학원에서 현직 백엔드 개발자 멘토님과 연결되어 실제 실무자의 시선으로 팀 전체의 코드를 리뷰받을 수 있었던 점입니다. 단순히 온라인 피드백이 아닌, 카페 등에서 팀원들과 직접 만나 기능 단위의 구조, 서비스 분리의 방향성, 객체지향적 설계 방식에 대한 리뷰를 수차례 진행했습니다. 이를 통해 단순한 동작만이 아니라“왜 이렇게 짜야 하는지”, “현실적인 유지보수성과 확장성은 어떤 기준에서 판단하는지” 등을 깊이 있게 배우며 실제 개발자처럼 사고하는 계기가 되었습니다.
무엇보다도 이 프로젝트를 통해 한 명의 개발자이자 리더로서의 책임감을 온전히 체감할 수 있었고, “혼자 잘하는 것”보다 “함께 완성도 높은 결과물을 만드는 것”의 가치를 배웠습니다. 전체를 조율하면서도 주요 기술과 도메인을 직접 구현한 경험은 앞으로 실무에서도 팀워크와 리딩 역량을 강하게 발휘할 수 있는 자신감으로 이어졌습니다.


👨🔧 어떤 문제가 있었고 어떻게 해결하였는가
① 문제 (Kafka 데이터 송수신 오류)
AnimalWar 프로젝트의 TerrainService가 Kafka를 통해 TerrainResponseDto를 전송하고, UserService가 해당 메시지를 수신해 유저 정보를 업데이트하는 구조를 구현하던 중, Kafka 메시지 송수신이 정상 작동하지 않는 문제가 발생했습니다. 메시지를 발행했음에도 Consumer에서 메시지를 받지 못하거나, 역직렬화 오류로 인해 처리에 실패하는 현상이 나타났습니다.
*역직렬화 : 문자열의 데이터를 다시 자바 객체로 되돌리는 과정
② 원인분석
1) Kafka 메시지 역직렬화 설정 오류
value-deserializer가 ByteArrayDeserializer로 설정되어 있어, Kafka에서 수신된 메시지를 객체(JSON) 로 변환하지 못했음.
이로 인해 DTO 객체 (TerrainResponseDto)가 자동 매핑되지 않고 예외가 발생
2) Enum 타입 직렬화
TerrainResponseDto 내부에 포함된 LandForm과 같은 Enum 타입이 Kafka 메시지로 전달될 때, Consumer 쪽에서 인식하지 못하면 역직렬화 에러가 발생.
③ 해결
1) Kafka 설정 수정
value-deserializer를 JsonDeserializer로 명시하고, DTO 클래스를 안전하게 역직렬화할 수 있도록 신뢰할 수 있는 패키지를 지정하였습니다. (Topic설정은 되어있었으나, application.yml 설정은 누락되었었음)

2) ENUM 타입 변경
기존에는 enum 옆에 설명을 포함하고 있는 형태였습니다.
public enum LandForm {
SEA("바다"),
LAND("육지"),
MOUNTAIN("산");
하지만 이런 description을 가지고 있는 경우 역직렬화가 안되기때문에, @JsonValue 및 @JsonCreator를 쓰는 방법도 있지만,
단순하게 문자열로만 이루어지게하여 역직렬화가 가능하게 구현하였습니다.
public enum LandForm {
SEA,
LAND,
MOUNTAIN
}
🍙 가장 신경쓴 부분
AnimalWar 프로젝트에서 제가 가장 많은 시간과 집중을 쏟은 부분은 Place.jsx에서의 타일 생성 및 배치 로직 구현입니다.
물론 제 역할은 백엔드 개발이었지만, 유저가 직접 보게 되는 게임 내 타일 맵이 백엔드 데이터와 정확히 일치하는지를 테스트하고 싶었습니다.
단순히 Postman으로 API 응답을 확인하는 것만으로는 한계가 있었기 때문에, 직접 Pixi.js 기반의 프론트 화면을 구현하여 시각적으로 확인하고자 했습니다.

백엔드에서 관리되는 Tile 클래스는 LandForm이라는 Enum을 통해 Sea, Land, Mountain의 지형 정보를 포함하고 있습니다.
이 데이터가 단순히 평면에 나열되는 것이 아니라, 입체적으로(등각 투영 방식으로) 표현된다면 유저 입장에서도 더 직관적이고 재미있는 경험을 제공할 수 있다고 판단했습니다.
const calculateMinIsoValues = (tileData, tileSize) => {
return tileData.reduce(
(minVals, data) => {
const isoX = ((data.x - data.y) * tileSize.width) / 2;
const isoY = ((data.x + data.y) * tileSize.height) / 4;
return {
x: Math.min(minVals.x, isoX),
y: Math.min(minVals.y, isoY),
};
},
{ x: Infinity, y: Infinity }
);
};
const minIsoValues = calculateMinIsoValues(tileData, tileSize);
등각 투영 방식에서는 좌표계가 비대칭이고 기울어져 있기 때문에, (0,0)을 기준으로 단순히 타일을 그릴 경우, 타일들이 한쪽으로 몰리거나 화면 바깥으로 나가는 현상이 생긴다는 것을 알게되었습니다.
그래서 전체 타일 중 가장 왼쪽 위 타일의 등각 위치를 기준점(minIsoValues)으로 정하고, 그 값을 이후 모든 타일 좌표에서 빼줘서 화면 중앙에 올바르게 배열할 수 있었습니다.
따라서, 각 타일의 실제 위치를 기준으로 가장 작은 isoX, isoY 값을 계산하여 minIsoValues를 구하고, 이를 기준점으로 삼아 맵을 정렬했습니다.
const isoX = ((data.x - data.y) * tileSize.width) / 2 - minIsoValues.x;
const isoY = ((data.x + data.y) * tileSize.height) / 4 - minIsoValues.y;
이 등각 투영 공식은 다음과 같은 구조로 구성됩니다:
x - y는 타일을 왼쪽 아래방향으로,
x + y는 타일을 오른쪽 아래방향으로 이동시키며,
타일의 넓이와 높이는 각각 / 2, / 4를 통해 기울기를 고려한 배치를 반영합니다.
이 수식 덕분에 각 타일이 마름모꼴로 렌더링되며, 실제 게임에서 보는 입체적인 구조처럼 보이게 됩니다.
그 결과, 동물이나 건물을 배치할 때 보다 생동감 있는 시각적 표현이 가능해졌고,
유저가 보는 화면에서도 각 타일의 지형 정보나 배치 상태를 직관적으로 이해할 수 있는 형태로 만들 수 있었습니다.
비록 백엔드 포지션이었지만, 데이터가 실제 화면에서 어떻게 해석되는지를 이해하는 노력이 중요하다고 느꼈고,
그 과정에서 좌표계, 등각 투영, 렌더링 최적화 등에 대해 깊이 고민할 수 있었던 값진 경험이었습니다.
'Project' 카테고리의 다른 글
회고) DevineEcho 프로젝트 [2024.11.14 - 2025.03.03] (1) | 2025.03.27 |
---|---|
SNS 사이트 구현하기 (임시) (0) | 2023.08.24 |
회고) BookBook 프로젝트 [2023.07.21 - 2023.08.03] (0) | 2023.08.03 |
회고) Mavve 프로젝트 [2023.06.15 - 2023.06.26] (0) | 2023.07.03 |