이번주는 팀빌딩부터 일본어 시험 준비까지 바쁜 한주였던것 같다.
팀은 같은 팀으로 서로가 장점이 다른 좋은 팀이 만들어진것 같다.
과제는 DOS 게임을 만들라고 했는데 난이도가 꽤 쉽다고(원래 개발자였던 내기준, 실제로는 좀 어려운 과제였다고 생각한다) 생각해서
좀 다른 방식으로 구현해보려고 했다.
근데 시간을 못맞출거 같아서 그냥 포기하고 AI로 만든 결과물로 제출했다 ㅠㅠ..
이번주는 귀찮아서 TIL을 작성 안했으니까 여기다가 이번주에 과제를 하면서 알게 된걸 정리해보려고 한다.
이번 과제는 Dart로 턴제 게임 만들기인데,
이미지와 같은 채팅 기반 턴제게임을 만드는 과제였다.
나는 그래서 옛날에 C언어로 Dos 게임을 만들어봤던 기억이 생각나서 비슷하게 한번 만들어볼까?? 그런 생각을 했다.
우선 DOS에서 글자에 색을 넣기 위해서는 DOS 색상문자인가?
// ANSI 컬러 코드 정의
const String reset = '\x1B[0m'; // 모든 속성 초기화
const String red = '\x1B[31m'; // 빨간색 전경색
const String green = '\x1B[32m'; // 초록색 전경색
const String yellow = '\x1B[33m'; // 노란색 전경색
const String blue = '\x1B[34m'; // 파란색 전경색
위와 같이 넣어야 한다고 해서 이건 좀 아니다 싶어서 라이브러리를 찾아보았다.
그래서 chalkdart, dart_console 라이브러리를 깔았다.
chalkdart는 문자열을 확장해서 색상을 입힐수 있다.
print('=== 🎮 ${'이름 설정'.green} 🎮 ===');
chalkdart와 dart_console을 사용해 방향키로 선택지를 선택하는 형식으로 만들어보았다.
주말에 일본어 시험이 없었다면 기한을 맞출 수 있을거 같았는데, 일본어 시험 준비하면서 하다보니까 일단 요구사항을 다 맞추지 못해서, 그냥 AI로 만든 코드로 제출했다.
이걸 만들면서 알게된 점은, 우선 Singleton 패턴이다.
이 코드를 짜면서 프론트엔드처럼 전역 상태관리를 어떻게 할 수 있을까? 하면서 찾아보다가 발견하였다.
[AI피셜]
싱글톤(Singleton) 패턴은 생성(Creational) 디자인 패턴 중 하나로, 특정 클래스의 인스턴스가 오직 하나만 존재하도록 보장하고, 해당 인스턴스에 대한 전역적인 접근 지점을 제공하는 패턴입니다.
쉽게 말해, 어떤 객체가 시스템 전체에서 단 하나만 존재해야 할 때 사용하는 방법입니다.
싱글톤 패턴의 주요 특징
- 단 하나의 인스턴스만 존재: 클래스에서 생성되는 객체가 무조건 한 개여야 합니다.
- 전역 접근 가능: 어디서든 해당 단일 인스턴스에 접근할 수 있는 방법을 제공합니다.
- 자기 통제: 인스턴스 생성을 스스로 통제하며, 외부에서 임의로 인스턴스를 생성하는 것을 막습니다.
왜 싱글톤 패턴을 사용할까요? (장점)
- 메모리 절약 및 효율성 증가: 인스턴스를 한 번만 생성하므로, 동일한 객체가 여러 번 생성되어 불필요하게 메모리를 낭비하는 것을 막습니다. 특히 데이터베이스 연결 풀, 스레드 풀, 설정 관리자 등과 같이 시스템 전체에서 공유되어야 하는 자원 관리에 유용합니다.
- 자원 관리 용이: 유일한 인스턴스를 통해 공유 자원에 대한 일관된 접근을 보장하고, 동시성 문제를 관리하기 쉬워집니다.
- 데이터 공유 용이: 전역으로 접근 가능한 인스턴스이기 때문에, 여러 클래스나 모듈 간에 데이터를 쉽게 공유할 수 있습니다.
싱글톤 패턴은 언제 사용될까요? (사용 사례)
- 설정 파일 관리자 (Configuration Manager): 애플리케이션의 설정 정보를 읽고 관리하는 객체는 하나만 존재해야 합니다.
- 로그 기록기 (Logger): 모든 로그 메시지를 하나의 파일이나 스트림에 기록할 때, 로거 객체는 하나만 있는 것이 효율적입니다.
- 데이터베이스 연결 풀 (Database Connection Pool): 여러 클라이언트가 데이터베이스에 접근할 때, 미리 생성된 연결을 재사용하여 효율성을 높입니다.
- 스레드 풀 (Thread Pool): 미리 생성된 스레드를 관리하고 재사용하는 객체.
어쨋든 처음 알게된 디자인 패턴을 활용하여 전역 상태를 관리할 수 있도록 구현하였다.
import 'package:game/model/character.dart';
import 'package:game/model/monster.dart';
class GameState {
// 싱글톤 인스턴스
static final GameState _instance = GameState._internal();
// 팩토리 생성자
factory GameState() {
return _instance;
}
// private 생성자
GameState._internal();
Character character = Character(
name: '',
gold: 0,
level: 1,
exp: 0,
maxExp: 100,
hp: 100,
maxHp: 100,
atk: 10,
def: 0,
);
int stage = 1;
int maxStage = 3;
int deadMonsterCount = 0;
List<Monster> monsterList = [];
Monster? nextMonster;
// 게임 설정
bool soundEnabled = true;
double soundVolume = 1.0;
// 상태 초기화
void reset() {
character = Character(
name: '',
gold: 0,
level: 1,
exp: 0,
maxExp: 100,
hp: 100,
maxHp: 100,
atk: 10,
def: 0,
);
stage = 1;
deadMonsterCount = 0;
monsterList = [];
nextMonster = null;
}
// 경험치 획득
void gainExp(int amount) {
character.exp += amount;
while (character.exp >= character.maxExp) {
levelUp();
character.exp = 0;
}
}
// 레벨업
void levelUp() {
character.level++;
character.exp -= character.maxExp;
character.maxExp = (character.maxExp * 1.2).round();
character.maxHp += 10;
character.hp = character.maxHp;
character.atk += 2;
character.def += 1;
}
// 골드 획득/사용
void addGold(int amount) {
character.gold += amount;
}
bool useGold(int amount) {
if (character.gold >= amount) {
character.gold -= amount;
return true;
}
return false;
}
// 체력 회복/감소
void heal(int amount) {
character.hp = (character.hp + amount).clamp(0, character.maxHp);
}
void takeDamage(int amount) {
character.hp = (character.hp - amount).clamp(0, character.maxHp);
}
// 현재 상태 출력용 메서드
Map<String, dynamic> getStatus() {
return {
'name': character.name,
'level': character.level,
'exp': '$character.exp/$character.maxExp',
'health': '$character.hp/$character.maxHp',
'attack': character.atk,
'defense': character.def,
'gold': character.gold,
};
}
}
다만 Singleton 패턴은 안티패턴이라고 평가받기도 한다고 하고, 나도 실제 개발에서 사용해본적은 없어서 그냥 지식 하나 쌓았다고 생각하고 넘어갈 것 같다.
[AI피셜]
싱글톤 패턴의 단점 (안티 패턴 논란)
싱글톤 패턴은 편리하고 유용하지만, 다음과 같은 단점으로 인해 "안티 패턴"으로 간주되기도 합니다.
- 강한 결합 (Tight Coupling): 싱글톤 인스턴스는 전역적으로 접근 가능하므로, 많은 모듈이 싱글톤에 직접 의존하게 됩니다. 이는 모듈 간의 결합도를 높여 코드 변경 시 파급 효과가 커질 수 있습니다.
- 테스트의 어려움: 싱글톤은 전역 상태를 가지므로, 단위 테스트 시 각 테스트가 독립적으로 실행되는 것을 방해할 수 있습니다. 이전에 실행된 테스트의 상태가 다음 테스트에 영향을 줄 수 있습니다.
- 단일 책임 원칙 (SRP) 위배: 싱글톤 클래스는 자신의 주요 책임 외에 "단일 인스턴스임을 보장하는 책임"까지 가지게 됩니다. 이는 단일 책임 원칙(Single Responsibility Principle)을 위반할 수 있습니다.
- 확장성 저해: 싱글톤은 단일 인스턴스를 강제하기 때문에, 나중에 여러 인스턴스가 필요한 상황이 생기면 코드 수정이 어려울 수 있습니다.
- 동시성 문제 (Concurrency Issues): 멀티스레드 환경에서 여러 스레드가 동시에 싱글톤 인스턴스에 접근하려고 할 때, 인스턴스가 두 개 이상 생성되거나 데이터 불일치 문제가 발생할 수 있습니다. (위의 Dart 예시는 late final로 인해 스레드 안전합니다. 하지만 다른 언어에서는 추가적인 동기화 처리가 필요할 수 있습니다.)
결론
싱글톤 패턴은 특정 자원이 시스템 전체에서 하나만 존재해야 할 때 유용하게 사용될 수 있습니다. 하지만 그 편리함 뒤에는 강한 결합, 테스트의 어려움 등의 단점도 존재하므로, 사용하기 전에 해당 패턴이 정말 필요한지 신중하게 고려해야 합니다. 요즘에는 의존성 주입(Dependency Injection)과 같은 다른 패턴이나 프레임워크를 사용하여 싱글톤의 장점을 유지하면서 단점을 완화하는 접근 방식을 더 선호하기도 합니다.
조금만 더 만들었으면 기한을 맞출 수 있었을거 같은데, 다음주부터는 Flutter 과정에 들어가니까, 그냥 즐겼다고 생각하고 넘어가야겠다.
나도 백엔드 자체는 실무경험이 많지 않아서 좀 더 여러가지에 대해 공부해야 겠다고 느꼇다.
끗-
'일기 > 스파르타코딩클럽 - 앱 창업' 카테고리의 다른 글
Weekend I Learned - `25.06.23 ~ `25.06.27 (0) | 2025.06.27 |
---|---|
Dart 문법 정리 / 객체지향프로그래밍 (클래스) - TIL (20250627) (1) | 2025.06.27 |
Dart 문법 정리 (함수형 프로그래밍) - TIL (20250626) (0) | 2025.06.26 |
Dart 문법 정리 (컬렉션) - TIL (20250625) (0) | 2025.06.25 |
Dart 문법 정리 (열거형) - TIL (20250624) (0) | 2025.06.24 |