데이터 구조 설계 (struct + mapping)
코드를 한 줄도 쓰기 전에 정할 것은 어떤 데이터를 어떻게 저장할지 입니다. 스마트 컨트랙트는 저장 구조를 잘못 잡으면 함수가 복잡해지고, 스토리지 비용(가스)이 늘고, 심지어 원하는 조회가 불가능해지기도 합니다. 반대로 설계가 좋으면 함수는 자연스럽게 짧고 단순해집니다.
투표 시스템에 필요한 데이터는 struct 와 mapping 두 가지로 나누어 담습니다.
후보자는 struct 로 묶는다
한 명의 후보자는 이름 과 득표 수 라는 서로 다른 타입의 정보를 함께 담습니다.
이렇게 성격이 다른 값을 하나의 단위로 묶을 때 가장 적합한 도구가 struct 입니다.
이름은 텍스트이므로 string, 득표 수는 0 이상의 정수이므로 uint256 으로 둡니다.
struct Candidate {
string name;
uint256 voteCount;
}후보자는 한 명이 아니라 여러 명이고, 등록 순서대로 번호(인덱스)를 매겨 관리하고 싶습니다.
순서가 있는 목록에는 동적 배열이 제격입니다. public 으로 두면 컴파일러가 자동 getter 를 만들어
외부에서 후보자 정보를 읽을 수 있습니다.
Candidate[] public candidates;투표 여부는 mapping 으로 추적한다
"이 주소가 이미 투표했는가?" 라는 질문은 주소를 키로, 불리언을 값으로 즉시 조회할 수 있어야 합니다.
배열로 매번 전체를 훑어 찾는 것은 비효율적이고 가스도 많이 듭니다. 이럴 때는 mapping 이 정답입니다.
mapping(address => bool) public hasVoted;hasVoted[msg.sender] 한 줄이면 호출자가 이미 투표했는지 O(1) 로 알 수 있습니다.
이 매핑이 중복 투표를 막는 modifier 의 핵심 재료가 됩니다.
배열은 "순서가 있는 목록", 매핑은 "키로 즉시 찾는 사전" 이라고 기억하면 둘 중 무엇을 쓸지 고르기 쉬워집니다. 후보자 목록은 배열, 투표 기록은 매핑이 자연스럽습니다.
매핑은 전체를 순회하거나 길이를 셀 수 없습니다. 그래서 "몇 명이 투표했는지" 같은 집계가 필요하면
별도의 카운터 변수를 따로 두어야 합니다. 반대로 배열은 .length 로 개수를 셀 수 있습니다.