컴퓨터 구조는 능숙한 개발자가 되기 위해 필수적인 기본 지식입니다. 컴퓨터 구조를 이해하면 문제 해결 능력이 향상되고 성능, 용량, 비용 등을 고려해 최적의 컴퓨터 환경을 선택할 수 있습니다.
이러한 구성 요소들이 함께 작동해 컴퓨터가 명령을 처리하고 데이터를 다룹니다.
격자 공간 분할: 맵을 일정한 크기의 격자 단위로 분할합니다. 각 격자(공간)는 특정 인구 밀도나 오브젝트의 활동 범위를 기준으로 설정됩니다. 이를 통해 작은 범위의 오브젝트는 해당 격자에만 동기화되고, 멀리 있는 다른 격자에 있는 오브젝트와 불필요하게 상호작용할 필요가 없어집니다.
예를 들어, 플레이어 A가 #1 공간에 있을 때, A의 움직임은 이 공간에 속한 다른 플레이어들에게만 전달됩니다. 플레이어가 공간 #1에서 #2로 이동하면, 이제는 공간 #2에 있는 플레이어들과만 상호작용이 이루어지게 됩니다.
Spatial Hashing: 각 오브젝트는 자신이 위치한 공간 번호(혹은 해시 값)를 가지고 있으며, 이 해시 값을 기준으로 동기화 대상이 되는 오브젝트 목록을 관리합니다. 이렇게 함으로써 전체 맵에서 일어나는 모든 이벤트를 브로드캐스팅하는 대신, 각 공간에서만 이벤트가 전달되므로 성능이 크게 향상됩니다.
이동 동기화: 플레이어가 한 공간에서 다른 공간으로 이동할 때, 동기화 대상 플레이어 목록이 갱신됩니다. 이전에 있던 공간의 플레이어들은 더 이상 해당 플레이어의 정보를 받을 필요가 없고, 새로 이동한 공간의 플레이어들만 해당 정보를 동기화받게 됩니다.
담당한 부분이 대부분의 기능이 구현되면 연결할 수 있어서 기다리는 동안 account 관리 모델과 핸들러를 구현하였다.
const Accounts = {};
export const createAccount = (uuid) => {
Accounts[uuid] = 0; // 새 계정 계좌를 0으로 초기화
};
export const getAccount = (uuid) => {
if (Accounts[uuid] === undefined) throw new Error('Account Not Found'); // 계좌 존재 여부 확인
return Accounts[uuid]; // uuid에 해당하는 계좌의 잔액 반환
};
export const updateAccount = (uuid, amount) => {
if (Number(amount) < 0) throw new Error('amount is less than 0');
Accounts[uuid] = amount;
return { status: 'success', message: 'Account update successful', balance: Accounts[uuid] };
};
export const deleteAccount = (uuid) => {
if (Accounts[uuid] === undefined) throw new Error('Account Not Found'); // 계좌 존재 여부 확인
delete Accounts[uuid]; // 계좌 삭제
return { status: 'success', message: 'Account delete successful' };
};
레디스로 구현하려다가 우선 틀만 잡고 추후에 팀원들과 상의 후 변경 예정이다.
import { getAccount, updateAccount } from '../models/account.model';
/**
* 계좌 입금
*
*
* @param {string} uuid uuid(userId)
* @param {number} amount 잔고에 입금하기 원하는 금액
* @returns {Object} 입금 결과를 반환하는 객체
*/
export const depositAccount = (uuid, amount) => {
try {
const balance = getAccount(uuid);
if (balance === undefined) {
throw new Error('Account not found');
}
const result = updateAccount(uuid, balance + amount); // 잔액 최신화
if (result.status === 'success') {
return result; // { status: 'success', message: 'Account update successful', balance: Accounts[uuid] }
} else {
throw new Error('Deposit failed');
}
} catch (err) {
return { status: 'fail', message: err.message };
}
};
/**
* 계좌 출금
*
*
* @param {string} uuid uuid(userId)
* @param {number} amount 잔고에 출금하기 원하는 금액
* @returns {Object} 출금 결과를 반환하는 객체
*/
export const withdrawAccount = (uuid, amount) => {
try {
const balance = getAccount(uuid);
if (balance === undefined) {
throw new Error('Account not found');
}
if (balance < amount) throw new Error('Insufficient Balance'); // 출금 가능 여부 확인
const result = updateAccount(uuid, balance - amount); // 잔액 최신화
if (result.status === 'success') {
return result; // { status: 'success', message: 'Account update successful', balance: Accounts[uuid] }
} else {
throw new Error('Withdraw failed');
}
} catch (err) {
return { status: 'fail', message: err.message };
}
};
/**
* 구매 가능 금액인지 확인
*
*
* @param {string} uuid uuid(userId)
* @param {number} amount 구매하기 원하는 아이템의 금액
* @returns {object} 충분한 잔액이 있는지 여부를 true/false로 반환
*/
export const hasSufficientBalance = (uuid, amount) => {
try {
const balance = getAccount(uuid);
if (balance === undefined) {
throw new Error('Account not found');
}
// 충분한 잔액이 있는지 여부를 true/false로 반환
return { status: 'success', sufficient: balance >= amount };
} catch (err) {
return { status: 'fail', message: err.message };
}
};
모델에서 다 처리할 수 있는 내용들이지만, 핸들러를 통해 접근하면 외부 노출로부터 안전하고 수정이 용이하다 하여 모델은 CRUD 기능만 넣었고 입금, 출금, 잔액 구매가능 여부 기능은 핸들러로 구현하였다.