문서 읽는 데 47분 · day02

day02

전체 21강 중 2강 · 자바 기초
난이도 · 입문

안녕하세요! 여러분의 Java 가이드, 홍순구 튜터입니다.

Day 2에 오신 걸 환영합니다! 지난 시간에 우리는 "Hello, World!"를 화면에 출력하면서 Java와 첫 인사를 나눴어요. 컴퓨터에게 말을 걸고, 결과를 눈으로 확인하는 경험을 했죠.

그런데 한 가지 아쉬운 점이 있었습니다. 우리가 출력한 "Hello, World!"는 그냥 지나가는 글자였어요. 프로그램이 그 글자를 기억하거나 보관하지 않았습니다.

인스타그램을 떠올려 보세요. 여러분의 사용자명, 나이, 팔로워 수, 비공개 계정인지 아닌지 — 이런 정보들을 앱이 기억하고 있잖아요? 프로그램이 데이터를 기억하고 보관하려면, 데이터를 담아둘 그릇이 필요합니다.

오늘의 주제는 바로 그 그릇, "변수와 자료형" 입니다.

🎯 학습 목표

  • 변수가 무엇인지 이해하고, 선언하고, 값을 바꿀 수 있다
  • Java의 기본 자료형 8가지 중 핵심 4가지(int, double, boolean, char)를 구분할 수 있다
  • String으로 문자열을 다루고, +로 연결할 수 있다
  • 자동 형변환과 강제 형변환의 차이를 설명할 수 있다
  • finalvar의 역할을 이해하고 적절히 사용할 수 있다
  • 여러 자료형을 조합해서 인스타그램 프로필 카드를 만들 수 있다

Step 1: "변수란 무엇인가 — 데이터를 담는 그릇"

지난 시간에 "프로그램이 정보를 기억하고 보관하려면 어떻게 해야 할까요?" 라는 질문을 던졌었죠. 오늘 그 답을 찾으러 갑니다. 바로 변수(Variable) 입니다.

변수를 왜 쓰나요?

여러분 집에 냉장고가 있죠? 냉장고에 우유를 넣어두면, 나중에 꺼내서 마실 수 있어요. 냉장고가 없다면? 우유를 사 오자마자 바로 마셔야 합니다. 보관할 수가 없으니까요.

프로그래밍에서 변수는 냉장고와 같습니다. 데이터를 넣어두면, 나중에 꺼내서 쓸 수 있어요. 변수가 없으면 데이터를 기억할 수 없고, 매번 새로 만들어야 합니다.

변수 만들기 — 선언과 할당

변수를 만드는 방법은 간단합니다. 어떤 종류의 그릇인지 정하고, 이름을 붙이고, 값을 넣으면 됩니다.

// day02/VariableBasic.java
void main() {
    // 변수 선언과 할당
    int age = 20;
    System.out.println(age);

    // 변수 값 변경
    age = 21;
    System.out.println(age);

    // 여러 변수 선언
    int birthYear = 2006;
    int currentYear = 2026;
    System.out.println(currentYear - birthYear);

    // 변수 이름 짓기 — camelCase
    int myFavoriteNumber = 7;
    System.out.println(myFavoriteNumber);
}

실행하면 이렇게 나옵니다.

20
21
20
7

코드를 한 줄씩 따라가 볼게요.

int age = 20; — 이 한 줄에 세 가지 정보가 담겨 있어요.

  • int : 이 그릇에는 정수(소수점 없는 숫자)를 담겠다는 뜻
  • age : 그릇의 이름age로 정했어요
  • = 20 : 그릇에 20이라는 값을 넣었어요

그 다음 줄에서 age = 21; 이라고 쓰면, 기존 그릇의 값을 20에서 21로 바꿉니다. 그릇이 "변하는 수"를 담고 있으니, 변수(Variable) 라고 부르는 거예요.

변수 이름 짓는 규칙 — camelCase

myFavoriteNumber라는 이름을 보면, 단어가 합쳐져 있는데 두 번째 단어부터 첫 글자를 대문자로 썼어요. 이런 이름 짓기 방식을 카멜케이스(camelCase) 라고 부릅니다. 낙타(camel) 등에 혹이 올라간 것처럼 생겨서 이런 이름이 붙었어요.

Java에서 변수 이름을 지을 때 지켜야 할 규칙이 몇 가지 있습니다.

  • 영문자, 숫자, _(밑줄), $를 쓸 수 있어요
  • 숫자로 시작하면 안 돼요1stPlace (X), firstPlace (O)
  • 띄어쓰기를 쓸 수 없어요my age (X), myAge (O)
  • 의미 있는 이름을 써야 해요 — a (X), age (O)
🙋 학생 질문 — "변수 이름을 한국어로 지으면 안 되나요?"

기술적으로는 가능합니다. int 나이 = 20; 이라고 써도 Java가 실행해 줘요. 하지만 실무에서는 영어 이름을 사용합니다. 전 세계 개발자들과 함께 코드를 읽고 쓰기 때문이에요. 지금부터 영어 이름에 익숙해지면 나중에 훨씬 수월합니다.

💡 튜터의 결론

변수는 데이터를 담아두는 그릇이에요. 자료형 이름 = 값; 세 가지만 기억하면 됩니다.


Step 2: "정수를 담는 그릇들 — int와 친구들"

변수가 "데이터를 담는 그릇"이라는 건 알겠는데, 그릇에도 크기가 있어요. 커피잔에 생수 2리터를 부으면 넘치겠죠? Java의 정수형 그릇도 크기가 다릅니다.

int — 가장 많이 쓰는 정수형

정수를 담을 때 가장 먼저 떠올리면 되는 타입int입니다. 영어 integer(정수)의 줄임말이에요.

대부분의 상황에서 int면 충분합니다. 약 21억까지 담을 수 있거든요.

long — 아주 큰 정수

그런데 세계 인구는 약 80억이에요. int의 한계(약 21억)를 넘어버리죠. 이럴 때는 더 큰 그릇인 long을 씁니다. 숫자 끝에 L을 붙여야 해요.

byte와 short — 작은 정수

byteshortint보다 작은 그릇입니다. 메모리를 아끼고 싶을 때 쓰지만, 실무에서는 intlong을 쓰는 경우가 대부분이에요. "이런 것도 있구나" 정도로 알아두면 됩니다.

// day02/IntegerTypes.java
void main() {
    // int — 가장 많이 쓰는 정수형
    int population = 51_000_000;
    System.out.println("대한민국 인구: " + population);

    // long — 아주 큰 정수 (끝에 L 붙이기)
    long worldPopulation = 8_000_000_000L;
    System.out.println("세계 인구: " + worldPopulation);

    // byte — 아주 작은 범위 (-128 ~ 127)
    byte smallNumber = 100;
    System.out.println("byte: " + smallNumber);

    // short — 작은 범위 (-32,768 ~ 32,767)
    short mediumNumber = 30000;
    System.out.println("short: " + mediumNumber);

    // 각 타입의 최댓값 확인
    System.out.println("int 최댓값: " + Integer.MAX_VALUE);
    System.out.println("int 최솟값: " + Integer.MIN_VALUE);
    System.out.println("long 최댓값: " + Long.MAX_VALUE);
}

실행 결과:

대한민국 인구: 51000000
세계 인구: 8000000000
byte: 100
short: 30000
int 최댓값: 2147483647
int 최솟값: -2147483648
long 최댓값: 9223372036854775807

코드에서 눈에 띄는 점 몇 가지를 짚어볼게요.

숫자 사이의 밑줄(_): 51_000_000처럼 숫자 사이에 밑줄을 넣을 수 있어요. 사람이 읽기 편하라고 쓰는 건데, Java는 밑줄을 무시하고 51000000으로 인식합니다. 우리가 "5,100만"이라고 쉼표를 찍는 것과 같은 역할이에요.

long에 붙는 L: 8_000_000_000L에서 끝의 L이 없으면 Java는 이 숫자를 int로 취급해서 에러가 납니다. "이 숫자는 int가 아니라 long이에요"라고 알려주는 표시예요.

Integer.MAX_VALUE: Java가 "이 타입에 담을 수 있는 최대 숫자가 얼마인지" 알려주는 기능이에요. int의 최댓값이 약 21억인 것을 직접 확인할 수 있습니다.

🙋 학생 질문 — "그냥 전부 long으로 쓰면 안 되나요? 큰 게 좋은 거 아닌가요?"

좋은 질문이에요! 가능은 합니다. 하지만 longint보다 메모리를 2배 더 차지해요 (8바이트 vs 4바이트). 나이, 점수, 개수처럼 21억을 넘을 일이 없는 숫자에 long을 쓰면, 메모리를 낭비하는 셈이에요. 실무에서는 필요한 만큼만 쓰는 것이 좋은 습관입니다.

지금은 "보통은 int, 아주 큰 숫자만 long" 이렇게 기억하면 충분해요.

💡 튜터의 결론

정수형은 int 하나만 확실히 기억하세요. 21억이 넘는 숫자가 필요할 때만 long을 꺼내면 됩니다.


Step 3: "소수점과 참/거짓 — double과 boolean"

정수만으로는 세상의 모든 데이터를 표현할 수 없어요. 키 175.5cm, 체온 36.5도 같은 소수점이 있는 숫자와, "학생인가요? 네/아니요" 같은 참/거짓 데이터도 다뤄야 합니다.

double — 소수점이 있는 숫자

소수점이 있는 숫자를 실수라고 부르는데, Java에서는 double이 대표 선수예요. 영어로 "두 배 정밀도(double precision)"라는 뜻인데, 지금은 "소수점 숫자 = double" 정도만 기억해 주세요.

float — double의 작은 버전

float도 소수점 숫자를 담는 타입인데, double보다 정밀도가 낮아요. 끝에 F를 붙여야 합니다. 요즘은 대부분 double을 쓰기 때문에, float는 참고만 하세요.

boolean — 참 또는 거짓

인스타그램에서 "비공개 계정인가요?", "인증된 계정인가요?" 같은 질문은 답이 네(true) 또는 아니요(false) 둘 중 하나죠. 이런 데이터를 담는 타입이 boolean입니다.

// day02/DoubleAndBoolean.java
void main() {
    // double — 소수점이 있는 숫자 (가장 많이 씀)
    double height = 175.5;
    System.out.println("키: " + height + "cm");

    double pi = 3.141592653589793;
    System.out.println("원주율: " + pi);

    // float — double보다 정밀도가 낮음 (끝에 F 붙이기)
    float temperature = 36.5F;
    System.out.println("체온: " + temperature + "도");

    // 소수점 계산의 함정
    System.out.println("0.1 + 0.2 = " + (0.1 + 0.2));

    // boolean — 참 또는 거짓만 담는 자료형
    boolean isStudent = true;
    boolean isGraduated = false;
    System.out.println("학생인가요? " + isStudent);
    System.out.println("졸업했나요? " + isGraduated);

    // boolean 값 바꾸기
    isGraduated = true;
    System.out.println("졸업했나요? (변경 후) " + isGraduated);
}

실행 결과:

키: 175.5cm
원주율: 3.141592653589793
체온: 36.5도
0.1 + 0.2 = 0.30000000000000004
학생인가요? true
졸업했나요? false
졸업했나요? (변경 후) true

잠깐, 결과에서 이상한 걸 발견하셨나요? 0.1 + 0.2의 결과가 0.3이 아니라 0.30000000000000004입니다!

이건 Java의 버그가 아니에요. 컴퓨터는 소수점 숫자를 2진수(0과 1) 로 저장하는데, 0.1이라는 숫자를 2진수로 정확하게 표현할 수 없어요. 마치 1/3을 소수로 쓰면 0.33333...이 끝없이 이어지는 것과 비슷합니다. 아주 작은 오차가 생기는 건데, 이건 Java뿐만 아니라 거의 모든 프로그래밍 언어에서 일어나는 현상이에요.

지금은 "소수점 계산에는 아주 작은 오차가 생길 수 있다"는 사실만 알아두면 충분합니다.

그리고 boolean을 보세요. isStudent, isGraduated처럼 이름이 "~인가요?" 형태죠. boolean 변수는 보통 is로 시작하는 이름을 붙여요. "학생인가요(isStudent)? 네(true)" — 이렇게 읽히니까 코드가 자연스럽습니다.

booleantrue/false는 다음 시간에 배울 조건문에서 아주 중요하게 쓰입니다. "이 값이 true이면 이쪽으로, false이면 저쪽으로" — 코드의 흐름을 갈라놓는 역할을 해요.

🙋 학생 질문 — "boolean이라는 이름은 왜 이렇게 생겼나요?"

조지 불(George Boole)이라는 19세기 수학자의 이름에서 따왔어요. 이 분이 "참/거짓만으로 논리를 표현하는 수학 체계"를 만들었는데, 그 업적을 기려서 이름이 붙었습니다. 그래서 Boolean이라고 대문자로 시작하는 경우도 있어요 (고유명사니까요).

💡 튜터의 결론

소수점 숫자는 double, 참/거짓은 boolean. 이 두 가지를 int에 더하면 벌써 세 가지 타입을 배운 거예요.


Step 4: "글자를 다루는 두 가지 — char와 String"

숫자와 참/거짓을 배웠으니, 이제 글자를 다뤄볼 차례예요. 인스타그램에서 사용자명이나 자기소개 같은 텍스트를 저장하려면, 글자를 담는 타입이 필요합니다.

char — 글자 딱 하나

charcharacter(글자)의 줄임말이에요. 딱 한 글자만 담을 수 있고, **작은따옴표(')**로 감싸야 합니다.

String — 글자 여러 개

현실에서 한 글자만 쓰는 경우는 거의 없죠. "홍길동", "안녕하세요!" 같은 문자열(글자들의 줄)을 담으려면 String을 씁니다. String은 **큰따옴표(")**로 감싸요.

// day02/CharAndString.java
void main() {
    // char — 글자 하나 (작은따옴표)
    char grade = 'A';
    char initial = '김';
    System.out.println("학점: " + grade);
    System.out.println("성: " + initial);

    // String — 글자 여러 개 (큰따옴표)
    String name = "홍길동";
    String greeting = "안녕하세요!";
    System.out.println(name);
    System.out.println(greeting);

    // char vs String — 따옴표가 다르다
    char singleChar = 'A';
    String singleString = "A";
    System.out.println("char: " + singleChar);
    System.out.println("String: " + singleString);

    // String 연결 (+)
    String firstName = "길동";
    String lastName = "홍";
    String fullName = lastName + firstName;
    System.out.println("이름: " + fullName);

    // 숫자와 String 연결
    int age = 20;
    String message = name + "은 " + age + "살입니다.";
    System.out.println(message);
}

실행 결과:

학점: A
성: 김
홍길동
안녕하세요!
char: A
String: A
이름: 홍길동
홍길동은 20살입니다.

여기서 중요한 차이점을 짚어볼게요.

따옴표 규칙: char는 작은따옴표('A'), String은 큰따옴표("A"). 같은 글자 A인데 따옴표가 다르면 Java가 완전히 다른 타입으로 인식합니다. 이건 자주 틀리는 실수이니 꼭 기억해 두세요.

+로 문자열 연결: String끼리 +를 쓰면 글자가 이어붙여집니다. "홍" + "길동""홍길동"이 되죠. 재미있는 건, String숫자+로 연결하면 숫자가 자동으로 글자로 바뀐다는 거예요. "홍길동은 " + 20 + "살입니다." — 이렇게 쓰면 20이 자동으로 "20"으로 변환됩니다.

String은 대문자로 시작: int, double, boolean, char는 모두 소문자로 시작하는데, String대문자 S로 시작하죠? 이건 String이 Java의 기본 타입이 아니라 특별한 타입이기 때문이에요. 자세한 이유는 나중에 클래스를 배울 때 알게 됩니다. 지금은 "String은 대문자로 시작한다"는 것만 기억하세요.

🙋 학생 질문 — "char를 실무에서 쓰는 경우가 있나요?"

솔직히 말하면, 실무에서 char를 직접 쓰는 경우는 많지 않아요. 대부분 String으로 처리합니다. 하지만 String을 한 글자씩 쪼개서 분석할 때 char가 등장하고, 학점이나 등급처럼 정말 한 글자만 필요한 경우에 가끔 쓰여요. "이런 타입이 있구나" 알아두면 나중에 코드를 읽을 때 당황하지 않습니다.

💡 튜터의 결론

글자 하나는 char(작은따옴표), 글자 여러 개는 String(큰따옴표). 실무에서는 거의 String을 씁니다.


Step 5: "그릇 바꿔 담기 — 형변환"

지금까지 int, double, boolean, char, String을 배웠어요. 그런데 프로그래밍을 하다 보면, 한 타입의 값을 다른 타입으로 옮겨야 할 때가 있습니다.

자동 형변환 — 작은 그릇에서 큰 그릇으로

종이컵에 담긴 물을 큰 텀블러에 옮겨 담는다고 생각해 보세요. 물이 넘칠 일이 없죠? 안전합니다.

Java도 마찬가지예요. 작은 타입의 값을 큰 타입에 넣으면, Java가 알아서 변환해 줍니다. 이걸 자동 형변환(Implicit Casting) 이라고 불러요.

intlongdouble 방향은 안전하니까 Java가 자동으로 해줍니다.

강제 형변환 — 큰 그릇에서 작은 그릇으로

반대로, 큰 텀블러의 물을 종이컵에 옮기면? 넘칠 수 있어요. Java는 이 상황이 위험하다는 걸 알기 때문에, 자동으로 해주지 않습니다. 개발자가 "넘칠 수 있다는 거 알고 있어, 그래도 해줘" 라고 직접 지시해야 해요. 이걸 강제 형변환(Explicit Casting) 이라고 하고, 타입 이름을 괄호로 감싸서 앞에 붙입니다.

// day02/TypeCasting.java
void main() {
    // 자동 형변환 — 작은 그릇에서 큰 그릇으로 (안전)
    int number = 100;
    long bigNumber = number;
    double decimal = number;
    System.out.println("int: " + number);
    System.out.println("int → long: " + bigNumber);
    System.out.println("int → double: " + decimal);

    // 강제 형변환 — 큰 그릇에서 작은 그릇으로 (명시적)
    double pi = 3.14159;
    int intPi = (int) pi;
    System.out.println("double: " + pi);
    System.out.println("double → int: " + intPi);

    // 데이터 손실 주의
    int bigValue = 300;
    byte smallValue = (byte) bigValue;
    System.out.println("int 300 → byte: " + smallValue);

    // long → int
    long longValue = 2_000_000_000L;
    int intValue = (int) longValue;
    System.out.println("long → int: " + intValue);

    // char와 int 사이의 변환
    char letter = 'A';
    int letterNumber = letter;
    System.out.println("'A'의 숫자 값: " + letterNumber);

    char fromNumber = (char) 66;
    System.out.println("66의 문자: " + fromNumber);
}

실행 결과:

int: 100
int → long: 100
int → double: 100.0
double: 3.14159
double → int: 3
int 300 → byte: 44
long → int: 2000000000
'A'의 숫자 값: 65
66의 문자: B

결과에서 흥미로운 점을 살펴볼게요.

int → double: 100100.0으로 바뀌었어요. 작은 타입에서 큰 타입으로 가니까 데이터 손실이 없습니다.

double → int: 3.141593이 됐어요. 소수점 아래가 잘려나갑니다. 반올림이 아니라 버림이에요!

int 300 → byte: 결과가 44? 이상하죠. byte의 범위는 -128~127인데, 300은 이 범위를 넘어요. 넘치는 물이 다시 처음부터 돌아가듯이, 300에서 256을 빼서 44가 된 겁니다. 이런 현상을 오버플로(overflow) 라고 불러요.

charint의 관계: 컴퓨터는 모든 글자에 고유한 번호를 매겨 저장해요. 'A'의 번호가 65, 'B'의 번호가 66입니다. 그래서 charint 사이에 변환이 가능한 거예요.

🙋 학생 질문 — "형변환을 왜 배워야 하나요? 처음부터 맞는 타입을 쓰면 되잖아요."

맞는 말이에요! 처음부터 적절한 타입을 쓰는 게 가장 좋습니다. 하지만 현실에서는 "다른 사람이 만든 코드에서 넘어온 값"이나 "계산 결과의 타입"이 내가 원하는 타입과 다른 경우가 자주 생겨요. 예를 들어 나눗셈 결과는 double인데, 화면에는 정수만 보여줘야 하는 상황 같은 거죠. 그때 형변환이 필요합니다.

💡 튜터의 결론

작은 타입 → 큰 타입은 자동, 큰 타입 → 작은 타입은 (타입) 을 붙여서 강제. 강제 형변환은 데이터가 잘려나갈 수 있으니 주의하세요.


Step 6: "변하지 않는 값과 똑똑한 추론 — final과 var"

지금까지 배운 변수는 값을 자유롭게 바꿀 수 있었어요. 그런데 어떤 값은 절대 바뀌면 안 되는 경우가 있고, 반대로 타입을 굳이 안 적어도 Java가 알아서 파악해주는 경우도 있습니다.

final — "이 값은 한번 정하면 끝이에요"

원주율 3.14159는 바뀌면 안 되겠죠? 이럴 때 final을 변수 앞에 붙이면, 값을 딱 한 번만 넣을 수 있어요. 이후에 바꾸려고 하면 Java가 에러를 내줍니다.

final 변수의 이름은 관례적으로 대문자와 밑줄로 적어요. PI, MAX_RETRY, GREETING 처럼요. "나는 변하지 않는 특별한 값이에요"라고 눈에 띄게 표시하는 거예요.

var — "타입을 알아서 파악해줘"

반대로, 매번 int, String, double 같은 타입을 적는 게 귀찮을 수 있어요. var를 쓰면 Java가 오른쪽에 넣은 값을 보고 타입을 알아서 추론해 줍니다.

var age = 25; — Java가 25를 보고 "아, int구나!" 하고 파악합니다.

// day02/FinalAndVar.java
void main() {
    // final — 변하지 않는 값 (상수)
    final double PI = 3.14159;
    final int MAX_RETRY = 3;
    final String GREETING = "안녕하세요";

    System.out.println("원주율: " + PI);
    System.out.println("최대 재시도: " + MAX_RETRY);
    System.out.println(GREETING);

    // final 변수는 변경 불가 — 아래 주석을 해제하면 에러!
    // PI = 3.14;

    // 상수를 활용한 계산
    int radius = 5;
    double area = PI * radius * radius;
    System.out.println("반지름 " + radius + "인 원의 넓이: " + area);

    // var — 타입 추론 (JDK 10+)
    var age = 25;
    var name = "홍길동";
    var height = 175.5;
    var isStudent = true;

    System.out.println("나이: " + age);
    System.out.println("이름: " + name);
    System.out.println("키: " + height);
    System.out.println("학생: " + isStudent);

    // var는 선언과 동시에 값을 줘야 함
    // var unknown;  // 에러 — 뭘로 추론할지 모름!
}

실행 결과:

원주율: 3.14159
최대 재시도: 3
안녕하세요
반지름 5인 원의 넓이: 78.53975
나이: 25
이름: 홍길동
키: 175.5
학생: true

finalvar는 역할이 완전히 달라요. 정리해 볼게요.

  • final : 값을 한 번 정하면 못 바꿈. 타입은 직접 적어야 해요.
  • var : 타입을 생략할 수 있음. 값은 나중에 바꿀 수 있어요.

그리고 // PI = 3.14; 주석을 한번 풀어서 직접 실행해 보세요. Java가 "cannot assign a value to final variable PI" 같은 에러를 보여줄 거예요. "final 변수에는 값을 다시 넣을 수 없습니다"라는 뜻이에요. 에러를 직접 확인해보면 더 오래 기억에 남습니다.

🙋 학생 질문 — "var가 편한데, 전부 var로 쓰면 안 되나요?"

기술적으로는 가능합니다. 하지만 코드를 읽는 사람 입장을 생각해 보세요.

var x = getUserAge(); — 이 코드를 처음 보는 사람은 xint인지 String인지 바로 알 수 없어요. int x = getUserAge(); — 이렇게 쓰면 "아, 정수구나" 바로 파악됩니다.

값이 뻔한 경우(var name = "홍길동")에는 var가 편리하고, 타입이 헷갈릴 수 있는 경우에는 타입을 직접 적는 게 읽기 좋아요. "적절히 섞어 쓰기"가 가장 현명한 방법입니다.

💡 튜터의 결론

final은 "절대 안 바뀌는 값"을 보호하고, var는 타입 적기를 생략해주는 편의 기능이에요.


Step 7: "종합 실습 — 인스타그램 프로필 카드"

지금까지 배운 모든 자료형을 한데 모아서, 인스타그램 프로필 카드를 만들어 봅시다. 이번 Step은 새로운 개념 없이, 배운 것들을 조합하는 연습이에요.

인스타그램 프로필 화면을 떠올려 보세요. 사용자명, 이름, 게시물 수, 팔로워 수, 팔로잉 수, 비공개 계정인지 여부 — 이런 정보들이 보이죠? 이 정보들을 변수로 선언하고 출력해 봅시다.

// day02/ProfileCard.java
void main() {
    // 인스타그램 프로필 카드 — 여러 자료형 종합
    String username = "hong_gildong";
    String displayName = "홍길동";
    int age = 20;
    double height = 175.5;
    boolean isVerified = false;
    boolean isPrivate = true;
    int postCount = 42;
    int followerCount = 1500;
    int followingCount = 300;

    // 프로필 출력
    System.out.println("=== 인스타그램 프로필 ===");
    System.out.println("사용자명: @" + username);
    System.out.println("이름: " + displayName);
    System.out.println("나이: " + age + "세");
    System.out.println("키: " + height + "cm");
    System.out.println("인증 계정: " + isVerified);
    System.out.println("비공개 계정: " + isPrivate);
    System.out.println("게시물: " + postCount);
    System.out.println("팔로워: " + followerCount);
    System.out.println("팔로잉: " + followingCount);
    System.out.println("========================");

    // 팔로워 비율 계산 (형변환 활용)
    double ratio = (double) followerCount / followingCount;
    System.out.println("팔로워/팔로잉 비율: " + ratio);

    // final로 제한 설정
    final int MAX_USERNAME_LENGTH = 30;
    System.out.println("사용자명 최대 길이: " + MAX_USERNAME_LENGTH);

    // var로 간결하게
    var bio = "Java를 배우는 중입니다!";
    System.out.println("자기소개: " + bio);
}

실행 결과:

=== 인스타그램 프로필 ===
사용자명: @hong_gildong
이름: 홍길동
나이: 20세
키: 175.5cm
인증 계정: false
비공개 계정: true
게시물: 42
팔로워: 1500
팔로잉: 300
========================
팔로워/팔로잉 비율: 5.0
사용자명 최대 길이: 30
자기소개: Java를 배우는 중입니다!

오늘 배운 것들이 전부 등장했어요! 하나씩 확인해 볼게요.

  • String : 사용자명(username), 이름(displayName)
  • int : 나이(age), 게시물 수(postCount), 팔로워 수(followerCount)
  • double : 키(height)
  • boolean : 인증 여부(isVerified), 비공개 여부(isPrivate)
  • 형변환 : (double) followerCount / followingCount — 정수끼리 나누면 소수점이 버려지니까, double로 변환해서 나눴어요
  • final : MAX_USERNAME_LENGTH — 절대 바뀌면 안 되는 제한값
  • var : bio — 오른쪽에 "문자열"이 있으니 Java가 String으로 추론

한 가지 주의할 점! followerCount / followingCount형변환 없이 그냥 나누면 어떻게 될까요? 1500 / 300 = 5 — 정수끼리의 나눗셈은 소수점을 버리고 정수만 남겨요. (double) followerCount / followingCount처럼 하나를 double로 바꿔줘야 5.0이 나옵니다. 이건 Step 5에서 배운 형변환이 실전에서 쓰이는 예시예요.

💡 튜터의 결론

한 프로그램 안에서 여러 자료형을 자유롭게 조합할 수 있어요. 어떤 데이터에 어떤 타입이 어울리는지 감을 잡는 게 이번 Step의 핵심입니다.


마무리

오늘 우리가 한 일을 돌아봅시다.

  1. 변수의 개념을 알게 됐어요 — 데이터를 담아두는 그릇, 자료형 이름 = 값;
  2. 정수형 intlong을 배웠어요 — 보통은 int, 아주 큰 숫자만 long
  3. 실수형 double논리형 boolean을 배웠어요 — 소수점과 참/거짓
  4. 문자 char문자열 String을 배웠어요 — 작은따옴표 vs 큰따옴표
  5. 형변환을 배웠어요 — 자동(안전)과 강제(위험)
  6. final(변하지 않는 값)과 var(타입 추론)를 배웠어요
  7. 인스타그램 프로필 카드로 전부 종합해 봤어요

지난 시간에는 화면에 글자만 출력했는데, 오늘은 데이터를 변수에 담아서 기억하고, 계산하고, 조합하는 데까지 왔습니다. 여러분의 프로그램이 점점 똑똑해지고 있어요!

다음 시간에는 이 변수들을 비교하고, 조건에 따라 다르게 동작하는 프로그램을 만들어 봅니다. 오늘 배운 booleantrue/false가 여기서 핵심 역할을 해요. 그리고 + 같은 기호를 연산자라고 부르는데, 다음 시간에 다양한 연산자를 본격적으로 만나봅니다!


과제

[실습] 나를 변수로 표현하기

이름(String), 나이(int), 키(double), 학생 여부(boolean) 등을 변수로 선언하고 출력하는 프로그램을 만들어 보세요.

요구사항:

  • 최소 4가지 자료형(int, double, boolean, String)을 각각 1개 이상 사용
  • 변수 이름은 camelCase로 의미 있게
  • 각 변수의 값을 System.out.println()으로 출력

[실습] 형변환 탐험

double price = 19999.99;int로 강제 형변환했을 때 어떤 값이 나오는지 예측해보고, 실제로 실행해서 확인하세요.

요구사항:

  • double 변수를 선언하고 int로 강제 형변환
  • 변환 전/후 값을 모두 출력
  • 왜 값이 달라졌는지 주석으로 설명

생각해볼 주제

1. "왜 자료형이 이렇게 많아야 할까?"

전부 String으로 저장하면 안 되는 이유는 뭘까요? 숫자도 글자처럼 "20"으로 저장하면, 편하긴 하겠지만 어떤 문제가 생길지 생각해 보세요.

2. "컴퓨터는 소수점 계산을 틀린다?"

0.1 + 0.2의 결과가 0.3이 아닌 0.30000000000000004였습니다. 컴퓨터가 이 간단한 계산을 "틀리는" 이유가 뭘까요? 그렇다면 은행 같은 곳에서 돈 계산을 할 때는 어떻게 해야 할까요?

3. "var를 쓰면 편한데, 왜 모든 곳에 쓰지 않을까?"

var를 쓰면 타이핑이 줄어들어서 편리합니다. 하지만 코드를 작성하는 시간보다 읽는 시간이 훨씬 길다는 말이 있어요. 가독성(읽기 쉬움)과 편의성 사이의 트레이드오프를 생각해 보세요.

✅ 예시 답안정답 보기

과제 1 예시답안: 나를 변수로 표현하기

핵심 접근

변수 선언의 목적은 "데이터에 이름표를 붙이는 것"이에요. 4가지 이상의 자료형을 골고루 써보면서, 각 자료형이 어떤 종류의 데이터를 담는 그릇인지 체감하는 게 이 과제의 핵심이에요. 변수 이름을 camelCase로 의미 있게 짓는 습관도 함께 연습해요.

예시 구현

void main() {
    String name = "홍길동";
    int age = 20;
    double height = 175.5;
    boolean isStudent = true;
    char bloodType = 'A';

    System.out.println("이름: " + name);
    System.out.println("나이: " + age + "세");
    System.out.println("키: " + height + "cm");
    System.out.println("학생 여부: " + isStudent);
    System.out.println("혈액형: " + bloodType + "형");
}

실행 결과

이름: 홍길동
나이: 20세
키: 175.5cm
학생 여부: true
혈액형: A형

채점 포인트

포인트 설명 배점 가중
4가지 자료형 사용 int, double, boolean, String을 최소 하나씩 사용했는가
camelCase 변수명 의미 있는 이름을 camelCase로 지었는가 (예: isStudent, bloodType)
출력 결과 System.out.println()으로 각 변수를 출력했는가
문자열 연결 + 연산자로 설명 텍스트와 변수를 연결했는가
추가 자료형 활용 char, long 등 요구사항 이상의 자료형을 시도했는가

흔한 실수

  • 변수명을 한글이나 단순 알파벳으로 지음 (이름, a, x) -- Java에서 한글 변수명이 문법적으로 가능하긴 하지만, 실무에서는 영어 camelCase가 표준이에요. 지금부터 습관을 들이면 좋아요.
  • String을 작은따옴표로 감쌈 (String name = '홍길동';) -- 작은따옴표(')는 char 전용이에요. 문자열(String)은 반드시 큰따옴표(")를 써야 해요.
  • boolean 값에 따옴표를 붙임 (boolean isStudent = "true";) -- true/false는 Java 키워드예요. 따옴표를 붙이면 문자열이 되어 타입 불일치 에러가 나요.

실무 개선 포인트 (심화)

  • printf 활용: System.out.printf("이름: %s, 나이: %d세%n", name, age); 처럼 서식 문자열을 쓰면 출력 형식을 더 정밀하게 제어할 수 있어요. 다만 이건 Day 2 범위를 넘으니 지금은 println + + 연결로 충분해요.
  • final 키워드: 혈액형처럼 변하지 않는 값에는 final char bloodType = 'A';로 선언하면 실수로 값을 바꾸는 사고를 막을 수 있어요.

과제 2 예시답안: 형변환 탐험

핵심 접근

강제 형변환(casting)에서 소수점이 어떻게 처리되는지 직접 예측하고 확인하는 게 핵심이에요. 특히 "반올림이 아니라 버림(절삭)"이라는 점, 음수일 때도 0 방향으로 절삭된다는 점을 코드로 확인해요.

예시 구현

void main() {
    double price = 19999.99;
    int intPrice = (int) price;

    System.out.println("원래 가격 (double): " + price);
    System.out.println("변환된 가격 (int): " + intPrice);
    // 소수점 이하가 버려져서 19999가 된다
    // 반올림이 아니라 버림!

    double temperature = -3.7;
    int intTemp = (int) temperature;
    System.out.println("원래 온도: " + temperature);
    System.out.println("변환된 온도: " + intTemp);
    // 음수도 소수점을 버린다: -3.7 -> -3
    // 0 방향으로 절삭 (truncation)
}

실행 결과

원래 가격 (double): 19999.99
변환된 가격 (int): 19999
원래 온도: -3.7
변환된 온도: -3

채점 포인트

포인트 설명 배점 가중
강제 형변환 문법 (int) 캐스팅 문법을 올바르게 사용했는가
변환 전/후 출력 원래 값과 변환된 값을 모두 출력해 비교했는가
주석 설명 왜 값이 달라졌는지 주석으로 설명했는가
절삭 이해 "반올림이 아니라 버림"이라는 핵심을 이해했는가
음수 테스트 음수 값도 변환해 0 방향 절삭을 확인했는가

흔한 실수

  • 반올림으로 착각 -- 19999.9920000이 될 거라고 예측하는 경우가 많아요. Java의 (int) 캐스팅은 소수점을 무조건 잘라내요. 반올림을 하고 싶다면 Math.round()를 써야 해요.
  • 캐스팅 괄호 위치 혼동 -- (int) price가 맞고, (int price)는 문법 에러예요. 캐스팅 연산자 (타입)은 변환할 값 바로 앞에 붙여요.
  • 음수 절삭 방향 혼동 -- -3.7-4가 될 거라고 예측하는 경우가 있어요. Java의 정수 캐스팅은 항상 0 방향으로 절삭해요. -3.7은 0에 더 가까운 -3이 돼요.

실무 개선 포인트 (심화)

  • 정밀한 반올림: 실무에서 가격 계산은 double 대신 BigDecimal을 사용해요. double은 이진 부동소수점이라 0.1 + 0.2가 정확히 0.3이 되지 않거든요. 금융/결제 도메인에서는 1원 차이가 큰 문제가 될 수 있어요.
  • 오버플로우 주의: double price = 3000000000.0;처럼 int 범위(약 21억)를 넘는 값을 (int)로 캐스팅하면 엉뚱한 음수가 나와요. 이런 경우 long을 써야 해요.

생각해볼 주제 1 예시답안: "왜 자료형이 이렇게 많아야 할까?"

[문제 상황 요약]

Java에는 int, double, boolean, String 등 여러 자료형이 있어요. "그냥 전부 String으로 저장하면 편하지 않을까?" 하는 의문이 자연스럽게 드는 시점이에요. 실제로 JavaScript나 Python 같은 언어는 타입을 덜 신경 써도 동작하니까요.

[튜터의 가이드 및 해설]

전부 String으로 저장하면 어떤 일이 벌어지는지 코드로 확인해볼게요.

void main() {
    // 전부 String으로 저장하면?
    String age = "20";
    String nextAge = age + 1;
    System.out.println(nextAge);  // "201" (숫자 덧셈이 아니라 문자열 연결!)

    // int로 저장하면?
    int ageNum = 20;
    int nextAgeNum = ageNum + 1;
    System.out.println(nextAgeNum);  // 21 (의도한 계산!)
}

이 예시 하나로 핵심이 보여요. String "20" + 1은 문자열 연결이 되어 "201"이 나오고, int 20 + 1은 의도한 대로 21이 나와요.

자료형이 여러 개인 이유는 크게 세 가지예요.

1. 연산의 의미를 보장해요

+ 연산자 하나가 자료형에 따라 완전히 다르게 동작해요. int끼리 +하면 덧셈, String끼리 +하면 문자열 연결이에요. 자료형이 명확해야 컴퓨터가 우리 의도대로 계산할 수 있어요.

2. 메모리를 효율적으로 써요

자료형 크기 용도
boolean 1 bit 수준 참/거짓 하나만 저장
int 4 byte 일반 정수
long 8 byte 큰 정수 (타임스탬프 등)
String 가변 텍스트

인스타그램에 사용자가 1억 명이라면, 나이를 int(4 byte)로 저장할 때와 String(가변, 보통 더 큼)으로 저장할 때 전체 메모리 차이가 커져요.

3. 실수를 컴파일 시점에 잡아줘요

int age = 20;
// age = "스물";  // 컴파일 에러! 숫자 변수에 문자열 넣기 불가

이런 실수를 프로그램이 실행되기 전에 잡아주니까, 실행 중에 갑자기 터지는 버그를 미리 방지할 수 있어요. 이걸 타입 안전성(Type Safety) 이라고 불러요.

🎯 면접관을 홀리는 핵심 멘트

"자료형은 단순한 분류가 아니라 연산의 의미를 보장하고, 메모리를 효율적으로 쓰고, 실수를 컴파일 시점에 잡아주는 세 가지 안전장치입니다. 전부 String으로 저장하면 \"20\" + 121이 아니라 \"201\"이 되는 것처럼, 의도와 다른 동작이 런타임에야 드러나서 디버깅 비용이 급격히 늘어납니다."


생각해볼 주제 2 예시답안: "컴퓨터는 소수점 계산을 틀린다?"

[문제 상황 요약]

수학 시간에 0.1 + 0.2 = 0.3은 당연한데, Java에서 실행하면 0.3이 아닌 값이 나와요. 컴퓨터가 계산을 못 하는 걸까요? 이건 Java만의 문제가 아니라 거의 모든 프로그래밍 언어에서 발생하는 현상이에요.

[튜터의 가이드 및 해설]

먼저 코드로 확인해볼게요.

void main() {
    double a = 0.1;
    double b = 0.2;
    double sum = a + b;

    System.out.println(sum);           // 0.30000000000000004
    System.out.println(sum == 0.3);    // false!
}

0.30000000000000004라는 미세한 오차가 생겨요. 왜 이런 일이 벌어질까요?

핵심 원리: 컴퓨터는 2진수로 소수를 표현해요

우리가 10진수로 1/3 = 0.3333...을 정확히 표현하지 못하는 것처럼, 컴퓨터는 2진수로 0.1을 정확히 표현하지 못해요. 0.1을 2진수로 바꾸면 0.0001100110011...처럼 무한히 반복되는데, 컴퓨터는 유한한 비트(double은 64비트)로 저장해야 하니까 어딘가에서 잘라내요. 이 잘린 오차가 계산을 거치면서 눈에 보이는 차이로 드러나는 거예요.

그래서 실무에서는 어떻게 할까요?

  • Option A: 오차 범위로 비교 -- == 대신 두 값의 차이가 아주 작은지 확인해요.
void main() {
    double sum = 0.1 + 0.2;
    double epsilon = 0.0001;   // 허용 오차

    // 두 값의 차이가 오차 범위 안이면 "같다"고 판단
    boolean isEqual = Math.abs(sum - 0.3) < epsilon;
    System.out.println(isEqual);  // true
}
  • Option B: BigDecimal 사용 -- 금액처럼 1원도 틀리면 안 되는 계산에서는 BigDecimal이라는 정밀 계산 도구를 써요.

지금 단계에서 BigDecimal을 쓸 필요는 없어요. 중요한 건 "소수점 비교에 ==을 쓰면 위험하다" 는 감각이에요.

현업에서는 보통: 일반적인 계산(좌표, 비율, 통계 등)은 double과 오차 범위 비교로 충분해요. 금융/결제처럼 정확한 금액 계산이 필요한 도메인에서만 BigDecimal을 사용해요.

🎯 면접관을 홀리는 핵심 멘트

"부동소수점 오차는 Java의 버그가 아니라 IEEE 754 표준의 구조적 한계입니다. 컴퓨터는 2진수로 소수를 표현하기 때문에 0.1처럼 2진수로 무한 소수가 되는 값은 정확히 저장할 수 없습니다. 그래서 소수점 비교에는 == 대신 오차 범위(epsilon) 비교를 쓰고, 금융 도메인처럼 정밀도가 중요한 곳에서는 BigDecimal을 사용합니다."


생각해볼 주제 3 예시답안: "var를 쓰면 편한데, 왜 모든 곳에 쓰지 않을까?"

[문제 상황 요약]

JDK 10부터 var를 쓰면 타입을 직접 적지 않아도 컴파일러가 알아서 추론해줘요. 코드가 짧아지니까 편한데, 그럼 항상 var만 쓰면 되지 않을까요?

[튜터의 가이드 및 해설]

var가 편한 경우와 오히려 불편해지는 경우를 비교해볼게요.

void main() {
    // var가 편한 경우: 오른쪽만 봐도 타입이 명확
    var name = "홍길동";        // String인 게 바로 보여요
    var age = 20;              // int인 게 바로 보여요
    var height = 175.5;        // double인 게 바로 보여요

    // var가 불편한 경우: 오른쪽만 봐도 타입을 모르겠음
    var result = calculate();  // 뭐가 나오는 거지? int? double? String?
    var data = process(input); // 무슨 타입이야?
}

var name = "홍길동"은 오른쪽의 "홍길동"만 봐도 String이라는 게 바로 보여요. 이런 경우에는 var가 코드를 깔끔하게 만들어줘요.

반면 var result = calculate()calculate()가 뭘 돌려주는지 메서드를 찾아가봐야 알 수 있어요. 혼자 작성한 코드라면 기억나겠지만, 다른 사람이 이 코드를 읽을 때 타입을 바로 알 수 없어서 이해하는 데 시간이 걸려요.

실무에서의 판단 기준은 "읽는 사람 입장"이에요.

상황 추천 이유
오른쪽에 타입이 명확히 드러남 var OK var list = new ArrayList<String>()
메서드 반환값 (타입 불분명) 타입 명시 String result = getName()
기본형 리터럴 둘 다 OK var age = 20 / int age = 20

Option A: "가독성 우선" 진영 -- 타입을 항상 명시해서 코드를 읽는 사람이 바로 이해할 수 있게 해요.

Option B: "간결함 우선" 진영 -- var를 적극 활용하되, IDE의 타입 힌트(마우스 올리면 타입 표시)에 의존해요.

현업에서는 보통: 팀마다 var 사용 범위를 코드 리뷰 규칙으로 정해두는 경우가 많아요. "오른쪽에서 타입이 바로 보이면 var OK, 아니면 타입 명시"라는 기준이 가장 흔해요. 결국 코드는 쓰는 시간보다 읽는 시간이 훨씬 길기 때문에, 읽는 사람을 배려하는 쪽이 유리해요.

🎯 면접관을 홀리는 핵심 멘트

"var는 타입 추론이지 타입 생략이 아닙니다. 컴파일 시점에 타입이 확정되므로 타입 안전성은 그대로 유지돼요. 다만 코드를 읽는 사람이 타입을 바로 파악할 수 있는지가 사용 기준입니다. 오른쪽 표현식에서 타입이 자명하면 var로 간결하게, 반환 타입이 불명확한 메서드 호출에서는 타입을 명시하는 게 팀 협업에서 유리합니다."

더 배우려면

실무 프로젝트까지 가고 싶다면

팀스파르타 백엔드 부트캠프에서 인스타그램 클론을 풀스택으로 완성합니다.