Chapter 03

Java 변수와 자료형

블록 세계 플레이어의 이름, 체력, 레벨을 저장하며 int, double, boolean, char, String과 final을 배웁니다.

난이도
입문
예상 학습
50분
Java 기준
Java 25 LTS
최종 검토

전체 22개 Chapter 확장 구조 중 Chapter 03입니다.

기능 구분

정식 Java 25에서도 쓸 수 있는 이전 버전 기능

Java SE 정식 기능

기본 자료형, String, final은 Java 25에서도 그대로 쓰는 기존 기능입니다.

이 Chapter의 예제는 Preview 또는 Incubator API 실행 옵션 없이 Java 25 LTS 정식 기능 기준으로 읽을 수 있습니다.

여러 블록 캐릭터 정보를 각각 이름표가 있는 보관함에 저장하는 Java 변수 학습 이미지
변수는 값마다 이름을 붙인 보관함입니다. 이름과 자료형을 보면 어떤 값이 들어 있는지 예상할 수 있어야 합니다.

Learning goals

오늘 배울 내용

  • 변수가 필요한 이유 설명하기
  • 값의 성격에 맞는 기본 자료형 선택하기
  • 변수 선언과 값 변경하기
  • final로 바뀌지 않는 규칙 표현하기

변수는 값에 붙이는 이름입니다

프로그램은 값을 다룹니다. 이름, 점수, 체력, 좌표, 성공 여부처럼 의미가 있는 값을 저장하고 다시 읽습니다. 같은 값을 여러 곳에 직접 적으면 수정하기 어렵고, 무엇을 뜻하는 숫자인지도 흐려집니다. 변수는 값에 이름과 자료형을 붙여 코드가 스스로 설명하도록 만듭니다.

Java의 변수 선언은 보통 다음 순서로 읽습니다.

자료형 변수이름 = 초기값;

예를 들어 int health = 18;은 정수 값을 저장할 health 변수를 만들고 처음 값으로 18을 넣는다는 뜻입니다. int는 값의 종류, health는 이름, 18은 실제 값입니다. 변수 이름을 잘 지으면 주석 없이도 “이 값은 체력이다”라고 읽을 수 있습니다.

기본형과 참조형의 첫 구분

입문 단계에서 가장 자주 쓰는 값은 숫자, 참거짓, 문자, 문자열입니다. Java에는 값을 직접 담는 기본형 primitive type이 있고, 객체를 가리키는 참조형 reference type이 있습니다. 당장 깊이 파고들 필요는 없지만 int, double, boolean, char는 기본형이고 String은 클래스라는 차이는 기억해 두세요.

  • int: 레벨, 개수, 체력처럼 일반적인 정수
  • long: int 범위를 넘을 수 있는 큰 정수
  • double: 속도, 비율, 좌표처럼 소수점이 필요한 수
  • boolean: 조건 결과를 담는 true 또는 false
  • char: 문자 하나
  • String: 여러 글자로 된 문자열

숫자처럼 보인다고 항상 숫자 자료형을 쓰지는 않습니다. 우편번호, 전화번호, 상품 코드처럼 계산하지 않고 식별자로 쓰는 값은 문자열이 자연스럽습니다. 반대로 화면에는 글자로 출력하더라도 비교, 증가, 합산을 한다면 숫자형을 써야 합니다.

플레이어 상태를 변수로 표현하기

다음 예제는 여러 자료형을 한 번에 사용합니다.

public class Main {
    public static void main(String[] args) {
        final int MAX_HEALTH = 20;
        final String WORLD_NAME = "Stone Garden";

        String playerName = "Rin";
        int level = 4;
        int health = 17;
        double walkSpeed = 4.25;
        boolean hasLantern = true;
        char teamCode = 'A';
        long visitedBlocks = 3_600_000_000L;

        health = health - 5;
        level = level + 1;

        System.out.println("월드: " + WORLD_NAME);
        System.out.println("플레이어: " + playerName);
        System.out.println("레벨: " + level + ", 팀: " + teamCode);
        System.out.println("체력: " + health + " / " + MAX_HEALTH);
        System.out.println("이동 속도: " + walkSpeed);
        System.out.println("랜턴 보유: " + hasLantern);
        System.out.println("방문 블록: " + visitedBlocks);
    }
}

예상 출력은 다음과 같습니다.

월드: Stone Garden
플레이어: Rin
레벨: 5, 팀: A
체력: 12 / 20
이동 속도: 4.25
랜턴 보유: true
방문 블록: 3600000000

health = health - 5;는 오른쪽의 기존 값을 먼저 읽고 5를 뺀 뒤, 그 결과를 다시 왼쪽 변수에 저장합니다. level = level + 1;도 같은 방식입니다. 변수는 한 번 선언한 뒤 값을 바꿀 수 있지만, final이 붙은 변수는 한 번 값이 정해지면 다시 대입할 수 없습니다.

final은 변경 금지보다 의도 표현입니다

final은 실수 방지 도구이면서 문서 역할도 합니다. MAX_HEALTHWORLD_NAME은 프로그램 실행 중 바뀌는 상태가 아니라 규칙에 가깝습니다. 이런 값에 final을 붙이면 이후 코드에서 잘못 대입했을 때 컴파일러가 막아 줍니다.

상수 이름은 관례적으로 대문자와 밑줄을 사용합니다. 모든 final 변수를 반드시 대문자로 써야 한다는 문법 규칙은 아니지만, “이 값은 규칙으로 읽어라”라는 팀의 신호가 됩니다.

final int INVENTORY_SIZE = 9;
final double TAX_RATE = 0.1;

지역 변수에 final을 붙이면 코드를 읽는 사람이 “이 값은 아래에서 바뀌지 않는다”고 믿을 수 있습니다. 특히 계산 과정이 길어질수록 바뀌는 값과 바뀌지 않는 값을 구분하는 일이 중요해집니다.

정수, 소수, 문자 리터럴 읽기

코드에 직접 적은 값을 리터럴이라고 합니다. 17, 4.25, 'A', "Rin"이 모두 리터럴입니다. long 리터럴에는 보통 대문자 L을 붙입니다. 소문자 l은 숫자 1과 헷갈리기 쉽기 때문입니다.

큰 숫자는 밑줄로 자리 구분을 할 수 있습니다.

long distance = 1_250_000_000L;
int dailyLimit = 10_000;

밑줄은 사람이 읽기 위한 표기이고 실제 값에는 영향을 주지 않습니다. 단, 숫자의 맨 앞이나 맨 뒤, 소수점 바로 옆처럼 허용되지 않는 위치가 있으므로 일반적인 자리 구분 용도로만 쓰는 것이 좋습니다.

char는 작은따옴표로 감싼 문자 하나입니다. "A"는 문자열이고 'A'는 문자입니다. 서로 비슷해 보여도 자료형이 다릅니다. 한 글자라도 이름, 코드, 메시지 일부처럼 문자열 API와 함께 다룰 가능성이 크면 String을 선택할 수 있습니다.

타입 추론 var는 언제 쓸까요?

Java에는 지역 변수에서 var를 사용할 수 있습니다. 컴파일러가 오른쪽 초기값을 보고 자료형을 추론합니다.

var campName = "North Gate";
var torchCount = 12;

위 코드는 각각 String, int로 추론됩니다. 다만 입문 단계에서는 자료형 이름을 직접 쓰는 편이 학습에 좋습니다. 타입을 읽는 훈련이 되기 때문입니다. var는 자료형이 너무 길거나 오른쪽에서 타입이 아주 분명할 때 코드 소음을 줄이는 용도로 사용하는 편이 좋습니다.

var는 아무 값 없이 선언할 수 없습니다. 컴파일러가 추론할 단서가 없기 때문입니다. 또한 필드나 메서드 매개변수에는 사용할 수 없고, 메서드 내부의 지역 변수에 사용합니다.

변수 이름 규칙과 좋은 이름

Java 변수 이름은 숫자로 시작할 수 없고, 공백을 포함할 수 없습니다. 관례적으로 lower camelCase를 사용합니다. 첫 단어는 소문자로 시작하고, 이후 단어의 첫 글자를 대문자로 씁니다.

int remainingTorchCount = 6;
boolean doorOpened = false;
String currentZoneName = "River Path";

a, temp, data 같은 이름은 짧지만 의미가 부족합니다. 반대로 theNumberOfTorchesThatPlayerCurrentlyHasInTheInventory처럼 너무 긴 이름도 읽기 어렵습니다. 좋은 이름은 값의 의미와 단위를 적당히 드러냅니다. speed보다 walkSpeed, time보다 elapsedSeconds가 더 분명합니다.

초기화와 대입 실수

메서드 안의 지역 변수는 읽기 전에 반드시 값이 있어야 합니다.

public class Main {
    public static void main(String[] args) {
        int score;
        score = 10;
        System.out.println(score);
    }
}

위 코드는 출력 전에 score에 값을 넣었기 때문에 괜찮습니다. 하지만 대입 없이 바로 출력하면 컴파일 오류가 납니다. Java가 “아직 정해지지 않은 값”을 실수로 읽는 상황을 막아 주는 것입니다.

대입 연산자 =와 비교 연산자 ==도 구분해야 합니다. health = 10은 값을 저장하는 명령이고, health == 10은 두 값이 같은지 확인하는 조건식입니다. 조건문 장에서 이 차이를 계속 사용합니다.

변수의 수명을 생각하기

변수는 선언된 위치에 따라 사용할 수 있는 범위가 달라집니다. 지금까지의 예제에서는 대부분 main 메서드 안에 변수를 선언했습니다. 이런 변수는 지역 변수라고 부르며, 선언된 블록 안에서만 사용할 수 있습니다. 중괄호 밖으로 나가면 그 변수 이름은 더 이상 유효하지 않습니다. 이 규칙은 코드를 안전하게 만듭니다. 작은 범위에서만 필요한 값이 프로그램 전체에 떠다니지 않기 때문입니다.

예를 들어 조건문 안에서만 필요한 메시지 변수는 조건문 블록 안에 둘 수 있습니다. 반대로 조건문 밖에서도 출력해야 하는 합계나 상태 값은 더 바깥쪽에 선언해야 합니다. 변수 선언 위치는 단순 취향이 아니라 코드의 의도를 드러냅니다. 가능한 한 필요한 가장 좁은 범위에 변수를 두면, 나중에 같은 이름을 잘못 재사용하거나 예상하지 못한 곳에서 값을 바꾸는 실수를 줄일 수 있습니다.

변수의 값이 언제 바뀌는지도 추적해야 합니다. 프로그램이 길어지면 같은 변수가 여러 번 대입될 수 있습니다. 이런 경우에는 바뀌는 변수와 바뀌지 않는 변수를 구분하는 것이 중요합니다. 바뀌지 않는 규칙 값에는 final을 붙이고, 바뀌는 상태 값에는 이름으로 변화 가능성을 드러내세요. remainingHealth, currentLevel, totalScore처럼 시간에 따라 달라질 수 있음을 보여 주는 이름이 도움이 됩니다.

자료형 선택은 요구사항 선택입니다

자료형은 메모리 모양만 결정하지 않습니다. 그 값으로 무엇을 할 수 있는지도 결정합니다. int는 정수 계산과 비교가 자연스럽고, double은 비율과 평균에 적합하지만 정밀한 금액 계산에는 주의가 필요합니다. boolean은 조건문의 재료가 되고, String은 사용자에게 보여 줄 문장이나 식별자를 담습니다. 자료형을 고르는 순간 코드의 사용 방식이 어느 정도 정해집니다.

입문자는 “나중에 필요하면 바꾸면 되지”라고 생각하기 쉽지만, 자료형이 바뀌면 연결된 연산과 출력과 조건도 함께 바뀔 수 있습니다. 그래서 변수를 만들기 전에 이 값이 개수인지, 비율인지, 식별자인지, 상태인지 한 번 질문해 보세요. 범위도 고려해야 합니다. 누적 방문 수, 밀리초 단위 시간, 대용량 데이터 개수처럼 커질 수 있는 값은 long을 검토하세요. 값의 의미에 맞는 가장 단순한 타입이 보통 좋은 선택입니다.

직접 해보기

  1. mana, coinCount, currentBiome 변수를 추가하고 출력합니다.
  2. MAX_HEALTH에 새 값을 다시 대입해 보고 어떤 컴파일 오류가 나는지 확인합니다.
  3. visitedBlocksL을 지웠을 때 컴파일러가 어떻게 반응하는지 확인합니다.
  4. var로 선언한 변수 두 개를 만들고, IDE나 오류 메시지로 추론된 타입을 관찰합니다.
  5. elapsedSeconds처럼 단위가 드러나는 변수 이름을 하나 만들어 봅니다.
점검 질문

값을 더하거나 비교할 예정인가요? 그렇다면 숫자형이나 boolean이 필요할 수 있습니다. 화면에 표시만 하고 계산하지 않는 식별자라면 String이 더 자연스러울 수 있습니다.

변수 복습 체크리스트

변수를 배운 뒤에는 선언, 읽기, 변경, 금지라는 네 동작을 구분할 수 있어야 합니다. 선언은 새 이름을 만드는 일입니다. 읽기는 변수 이름으로 현재 값을 가져오는 일입니다. 변경은 오른쪽 계산 결과를 다시 변수에 저장하는 일입니다. 금지는 final을 사용해 두 번째 대입을 막는 일입니다.

다음 질문에도 답해 보세요. Stringchar는 어떻게 다른가요? long 리터럴에 L을 붙이는 이유는 무엇인가요? 지역 변수를 초기화하지 않고 읽으면 왜 오류가 날까요? final은 실행 중 값을 보호하는 것 외에 코드 읽기에 어떤 도움을 줄까요?

변수는 이후 모든 장에서 계속 등장합니다. 조건문은 변수 값을 비교하고, 반복문은 변수 값을 바꾸며, 배열은 여러 값을 한 변수 이름 아래 묶습니다. 그래서 변수 이름을 성의 있게 짓는 습관은 초반부터 중요합니다. 좋은 이름은 타입과 함께 값의 의미를 알려 주고, 나쁜 이름은 코드를 읽는 사람에게 매번 추측을 요구합니다.

작은 리팩터링 연습

리팩터링은 동작을 유지하면서 코드의 구조와 이름을 개선하는 일입니다. 입문 단계에서도 아주 작게 연습할 수 있습니다. 숫자 20이 여러 줄에 반복된다면 final int MAX_HEALTH = 20;으로 바꿔 보세요. x, y가 좌표인지 속도인지 모호하다면 playerX, playerY처럼 의미를 드러내 보세요. 출력문이 길다면 중간 문자열 변수를 만들어 읽기 쉽게 만들 수도 있습니다.

중요한 점은 한 번에 하나씩 바꾸고 실행 결과를 확인하는 것입니다. 이름을 바꾼 뒤 컴파일하고, 상수를 뽑은 뒤 컴파일하고, 자료형을 바꾼 뒤 컴파일합니다. 이 습관은 큰 프로젝트에서도 그대로 유효합니다.

이번 장의 정리

변수는 값에 이름과 타입을 붙이는 장치입니다. Java는 타입을 분명히 요구하기 때문에 컴파일 전에 많은 실수를 잡을 수 있습니다. final은 바뀌지 않는 규칙을 표현하고, 좋은 변수 이름은 코드의 설명력을 높입니다. 다음 장에서는 변수에 담긴 값을 계산하고 비교하는 연산자를 배웁니다.

FAQ

자주 묻는 질문

String은 왜 소문자 string이 아닌가요?

String은 Java가 제공하는 클래스 이름이므로 대문자로 시작합니다. int와 boolean 같은 기본형과 구분됩니다.

int와 long 중 무엇을 써야 하나요?

일반적인 체력이나 레벨은 int로 충분합니다. int 범위를 넘을 수 있는 큰 정수에는 숫자 뒤 L을 붙인 long을 사용합니다.

변수 이름에 한글을 써도 되나요?

문법상 가능하지만 팀과 도구의 호환성을 위해 의미가 분명한 영문 camelCase를 일반적으로 사용합니다.

작성·편집 ToolPado

콘텐츠 유형 Java 학습 가이드

Java 기준 Java 25 LTS

마지막 검토