오늘 학습의 큰 흐름

reference/javastudy/day0106/src/javaStudy에는 Study01.java부터 Study21.java까지 들어 있다. 첫날이 변수와 자료형을 익히는 날이었다면, 둘째 날은 “입력받은 값을 가지고 프로그램 흐름을 제어하는 법”을 배우는 날이었다.

이날 공부한 내용은 단순히 문법 항목이 많은 것이 아니라 서로 연결돼 있다.

  1. 사용자에게 값을 입력받는다.
  2. 그 값을 숫자로 바꾼다.
  3. 연산자를 사용해 계산하거나 비교한다.
  4. 조건문으로 경우를 나눈다.
  5. 반복문으로 같은 작업을 여러 번 수행한다.

즉, 이 날부터 비로소 “프로그램다운 프로그램”의 형태가 만들어지기 시작했다.

1. 출력과 입력: 프로그램이 사용자와 대화하기 시작했다

Study01.javaprintln, print, printf 같은 기본 출력 방식을 비교하는 실습이다. 초반에는 사소해 보이지만, 콘솔 프로그램을 만들 때 줄바꿈이 붙는지, 같은 줄에 이어서 출력되는지 아는 것은 중요하다.

  • println(): 출력 후 줄바꿈
  • print(): 줄바꿈 없이 출력
  • printf(): 형식 지정 출력

이후 Study02.java에서 본격적으로 Scanner가 등장한다.

Scanner scanner = new Scanner(System.in);
System.out.print("x 값 입력:");
String strX = scanner.nextLine();
int x = Integer.parseInt(strX);

여기서 중요한 포인트는 두 가지다.

  • Scanner는 입력을 “문자열”로 받아온다.
  • 계산을 하려면 그 문자열을 숫자로 바꿔야 한다.

예를 들어 사용자가 10을 입력했다고 해도, 그냥 "10" 상태로 두면 문자열이다. 이 상태에서는 5를 더하는 수치 계산이 아니라 문자열 연결이 될 수 있다. 그래서 Integer.parseInt() 같은 변환 과정이 필요하다.

nextLine()parseInt()를 같이 쓸까

입문 단계에서는 scanner.nextInt()도 자주 쓰지만, 실제로는 nextLine()으로 한 줄 전체를 받은 뒤 직접 변환하는 방식이 더 안전한 경우가 많다. 이유는 입력 버퍼 문제 때문이다. nextInt()nextLine()을 섞어 쓰면 줄바꿈 문자가 남아 예상치 못한 동작이 발생하기 쉽다.

이 날 실습에서 문자열로 받고 숫자로 바꾸는 흐름을 익힌 것은 이후 프로그램에서도 계속 도움이 된다.

2. 부호 연산과 증감 연산: 값이 어떻게 바뀌는지 읽는 연습

Study03.java는 부호 연산을 다룬다. x = -x; 같은 코드는 단순해 보이지만, 작은 타입이 연산 과정에서 int로 승격된다는 사실을 함께 보여 준다.

byte b = 100;
int y = -b;

여기서 bbyte지만, -b를 계산하는 순간 결과는 int 취급이 된다. Java는 이런 식으로 연산 중 타입을 확장하는 규칙이 꽤 엄격하다.

Study04.java는 전위 증가와 후위 증가를 보여 준다.

  • x++: 현재 값을 먼저 사용한 뒤 증가
  • ++x: 먼저 증가시키고 그 값을 사용

처음엔 둘 다 “1 증가”처럼 보이지만, 다른 식 안에 들어가면 결과가 달라진다.

예를 들어:

int x = 10;
int y = x++;

이 경우 y에는 10이 들어가고, 그 뒤에 x가 11이 된다.

반대로:

int x = 10;
int y = ++x;

이 경우 x가 먼저 11이 된 뒤 y에도 11이 들어간다.

이 차이를 초반에 이해하지 못하면, 조건식이나 반복문에서 이상한 버그를 만들기 쉽다.

3. 산술 연산과 타입 규칙: 계산은 되는데 왜 타입 오류가 날까

Study05.java는 산술 연산 결과의 타입을 익히는 예제다. Java에서 작은 타입끼리 계산해도 결과는 대부분 int로 올라간다.

byte v1 = 10;
byte v2 = 4;
int result1 = v1 + v2;

왜 결과를 byte가 아니라 int에 넣을까? Java는 계산 과정에서 오버플로우 위험을 줄이고 일관된 연산 규칙을 유지하기 위해 기본적으로 int 단위 연산을 사용하기 때문이다.

초보자가 자주 하는 실수

byte result = v1 + v2; // 오류 가능

이렇게 쓰면 컴파일 오류가 날 수 있다. 값이 작아 보여도 자바는 “연산 결과는 int”라고 판단하기 때문이다.

즉, “값의 크기”가 아니라 “언어 규칙”을 따라야 한다.

4. 부동소수점: 계산은 맞는 것 같은데 결과가 이상한 이유

Study07.java는 사과 1개를 0.1 조각 단위로 계산하는 예제다.

int apple = 1;
double pieceUnit = 0.1;
int number = 7;
double result = apple - number * pieceUnit;

사람은 1 - 0.7 = 0.3을 기대하지만, 실제 출력은 아주 미세하게 어긋난 숫자가 나올 수 있다. 이유는 0.1이 컴퓨터 내부에서 정확히 표현되지 않기 때문이다.

왜 이런 일이 생기나

컴퓨터는 2진수로 실수를 저장한다. 그런데 10진수의 0.1은 2진수로 딱 떨어지지 않는다. 그래서 근사값으로 저장되고, 연산이 누적되면 오차가 보인다.

이 실습이 중요한 이유는 단순히 “자바가 이상하다”가 아니라, 부동소수점 전체의 특성을 이해하게 해 주기 때문이다. 나중에 금액 계산처럼 정확도가 중요한 곳에서는 double 대신 BigDecimal 같은 방법을 고려해야 한다.

5. 0으로 나누기, Infinity, NaN

Study08.java에서는 실수 연산의 특이한 결과를 보여 준다.

  • 5 / 0.0 같은 연산은 Infinity
  • 5 % 0.0 같은 연산은 NaN

여기서 NaN은 “Not a Number”를 뜻한다. 숫자처럼 보이지만 유효한 수치 결과가 아니라는 의미다.

이 구간에서 중요한 포인트는 “실수 나눗셈은 정수 나눗셈과 다르게 에러 없이 특수값이 나올 수 있다”는 점이다. 그래서 계산 전에 Double.isInfinite()Double.isNaN() 같은 검사를 해 줄 필요가 있다.

6. 비교 연산자: 참과 거짓을 만들어 내는 연산

Study09.java는 비교 연산자를 정리한다.

  • ==: 같다
  • !=: 다르다
  • >: 크다
  • <: 작다
  • >=: 크거나 같다
  • <=: 작거나 같다

비교 연산의 결과는 항상 boolean이다. 즉 true 또는 false가 된다.

int num1 = 10;
int num2 = 10;
boolean result1 = (num1 == num2);

이 단계에서 중요한 건 “조건문 안에는 결국 boolean 값이 들어간다”는 점을 이해하는 것이다. 나중에 if, while, for를 읽을 때 핵심이 된다.

7. 논리 연산자: 조건을 묶는 법

Study10.java는 논리 연산을 다룬다.

  • &&: 그리고
  • ||: 또는
  • !: 부정

예를 들어 “대문자냐?”를 판별하려면 하나의 비교만으로는 부족하다. 특정 범위 안에 있는지 두 조건을 함께 검사해야 한다.

if ((charCode >= 65) && (charCode <= 90)) {
    System.out.println("대문자이군요");
}

이 식을 말로 풀면 이렇다.

  • charCode가 65 이상이고
  • 동시에 90 이하이면
  • 대문자다

단락 평가도 알아 둘 것

&&||는 앞 조건만으로 결과가 결정되면 뒤 조건을 평가하지 않는다.

  • false && ...는 뒤를 볼 필요가 없다.
  • true || ...도 뒤를 볼 필요가 없다.

이 특성은 성능에도 영향을 주고, null 검사 같은 안전한 코드 작성에도 매우 중요하다.

8. 비트 연산자는 왜 배우나

Study11.java&, |, ^, ~ 같은 비트 연산을 보여 준다. 입문 단계에서는 낯설 수 있지만, 숫자를 2진수 수준에서 다루는 연산이다.

  • &: AND
  • |: OR
  • ^: XOR
  • ~: 비트 반전

이 개념은 일상적인 CRUD 앱에서는 자주 안 쓰일 수 있지만, 권한 플래그 처리, 저수준 시스템, 성능 최적화 코드 등에서는 여전히 중요하다. 지금 당장은 “이런 연산도 있고, 일반 논리 연산과는 다르다” 정도만 이해해도 충분하다.

9. 복합 대입 연산자: 코드를 짧게 쓰되 의미는 분명하게

Study12.java+=, -=, *=, /=, %= 같은 복합 대입 연산자를 다룬다.

result += 10;

이 코드는 사실 아래와 같은 의미다.

result = result + 10;

이 연산자는 코드를 짧게 만드는 용도만 있는 것이 아니다. 누적합, 카운트 증가, 점수 계산처럼 현재 값을 기준으로 계속 바뀌는 로직에서 가독성을 높여 준다.

10. 삼항 연산자: 짧은 조건문

Study13.java는 삼항 연산자를 사용해 점수에 따라 학점을 구한다.

char grade = (score >= 90) ? 'A' :
             (score >= 80) ? 'B' :
             (score >= 70) ? 'C' :
             (score >= 60) ? 'D' : 'F';

삼항 연산자는 짧고 간단한 조건 분기에는 편리하지만, 너무 길어지면 오히려 읽기 어려워진다. 이 예제는 삼항 연산자가 가능하다는 걸 보여 주는 데는 좋지만, 실제로는 가독성을 위해 if-else가 더 나을 때도 많다.

즉, “짧다고 무조건 좋은 코드”는 아니다.

11. if문: 가장 기본적인 분기 구조

Study14.javaStudy15.java는 점수를 입력받아 등급을 나누는 흐름이다. 사용자 입력을 받고, 조건에 따라 다른 결과를 출력한다는 점에서 아주 전형적인 입문 예제다.

if문의 구조는 이렇다.

if (조건1) {
    실행문1
} else if (조건2) {
    실행문2
} else {
    실행문3
}

이때 중요한 것은 위에서 아래로 순서대로 검사한다는 점이다. 즉, 앞 조건이 이미 참이면 뒤 조건은 보지 않는다.

예를 들어:

if (score >= 90) {
    grade = 'A';
} else if (score >= 80) {
    grade = 'B';
}

점수가 95라면 첫 번째 조건에서 이미 결정된다. 그래서 80 이상 조건은 아예 검사하지 않는다.

if문에서 자주 하는 실수

  • 조건 순서를 잘못 배치하는 것
  • ===를 혼동하는 것
  • 범위를 빠뜨리는 것

예를 들어 먼저 score >= 60을 검사해 버리면, 90점도 그 조건에 걸려서 A가 아니라 D처럼 처리될 수 있다. 그래서 조건은 일반적으로 더 엄격한 것부터 써야 한다.

12. switch문: 같은 대상을 여러 값과 비교할 때 유리하다

Study16.java, Study17.java, Study18.java는 주사위 결과나 문자 등급을 switch로 분기한다.

switch (num) {
    case 1:
        System.out.println("1번 나옴");
        break;
    case 2:
        System.out.println("2번 나옴");
        break;
    ...
}

switch는 하나의 값을 여러 case와 비교할 때 가독성이 좋다.

꼭 기억해야 할 break

break를 쓰지 않으면 다음 case로 계속 흘러간다. 이를 fall-through라고 한다.

예를 들어:

case 'A':
case 'a':
    System.out.println("우수 회원입니다.");

이 구조는 오히려 의도적으로 break를 생략한 좋은 예다. 대문자 A든 소문자 a든 같은 결과를 내기 위해 두 case를 묶은 것이다.

즉, break 누락이 항상 실수는 아니지만, 의도 없이 빠지면 버그가 된다.

13. for문: 정해진 횟수만큼 반복하기

Study19.javaStudy20.javafor문의 기초를 보여 준다.

기본 구조는 다음과 같다.

for (초기식; 조건식; 증감식) {
    반복 실행문
}

예를 들어:

for (int i = 1; i <= 10; i++) {
    System.out.println(i);
}

이 코드를 말로 풀면 다음과 같다.

  1. i를 1로 시작한다.
  2. i <= 10인지 검사한다.
  3. 참이면 본문을 실행한다.
  4. i++로 1 증가시킨다.
  5. 다시 조건을 검사한다.

Study20.java의 1부터 100까지 합 구하기는 누적 변수를 반복문과 함께 쓰는 가장 기본적인 패턴이다.

int sum = 0;
for (i = 1; i <= 100; i++) {
    sum += i;
}

이 패턴은 이후 평균 구하기, 총점 계산, 개수 세기 등 거의 모든 기초 알고리즘의 출발점이 된다.

14. 반복문 안에 반복문: 패턴 출력의 진짜 목적

Study21.java는 별을 출력하는 예제다.

*
**
***
****
*****

처음 보면 그냥 별 찍기 문제처럼 보이지만, 사실 이 예제의 진짜 목적은 중첩 반복문을 이해하는 것이다.

예를 들어 바깥 반복문이 “줄 수”를 담당하고, 안쪽 반복문이 “그 줄에서 별 몇 개를 찍을지”를 담당한다. 이 구조를 이해하면 이후 표 출력, 좌표 계산, 2차원 배열 순회 같은 문제를 훨씬 쉽게 풀 수 있다.

즉, 별 자체가 중요한 게 아니라 “반복의 반복”을 읽는 능력이 중요하다.

15. 오늘 실습을 통해 얻은 핵심 감각

둘째 날의 실습을 한 문장으로 요약하면 이렇다.

“값을 받아서 판단하고, 같은 규칙을 반복 실행하는 법을 배웠다.”

이날 코드에서 얻은 핵심 감각은 다음과 같다.

  • 입력은 대부분 문자열로 들어오므로 변환이 필요하다.
  • 연산자는 단순 계산 기호가 아니라 타입 규칙까지 함께 따른다.
  • 조건문은 결국 boolean 값을 기준으로 흐른다.
  • 반복문은 초기값, 조건, 변화량을 같이 읽어야 한다.
  • 간단한 예제라도 이미 프로그램의 구조를 갖추고 있다.

오늘의 복습 질문

이 글을 읽고 바로 답할 수 있으면 day2 내용은 꽤 잘 정리된 것이다.

1. Scanner로 입력받은 값을 바로 계산하지 않고 parseInt() 하는 이유는 무엇일까?

입력이 문자열로 들어오는 경우가 많기 때문이다. 계산하려면 숫자 타입으로 바꿔야 하므로 parseInt() 같은 변환이 필요하다.

2. x++++x는 언제 다른 결과를 만들까?

다른 식 안에 함께 들어갈 때 차이가 난다. x++는 현재 값을 먼저 사용한 뒤 증가하고, ++x는 먼저 증가한 뒤 그 값을 사용한다.

3. double 계산에서 0.1이 정확히 떨어지지 않을 수 있는 이유는 무엇일까?

부동소수점은 10진 소수를 2진수로 완벽하게 표현하지 못하는 경우가 있기 때문이다. 그래서 미세한 오차가 생길 수 있다.

4. ifswitch는 각각 어떤 상황에서 더 읽기 쉬울까?

if는 범위 비교나 복잡한 조건식에, switch는 값이 몇 가지 경우로 딱 나뉘는 분기에서 더 읽기 쉽다.

5. for (int i = 1; i <= 10; i++)를 말로 설명할 수 있을까?

i를 1에서 시작해 10 이하인 동안 1씩 증가시키며 반복하라는 뜻이다. 즉, 1부터 10까지 차례대로 같은 작업을 수행한다.

마무리

1월 6일 학습은 입문 자바에서 아주 중요하다. 첫날 배운 변수와 자료형이 이 날부터 실제 흐름 제어 도구와 결합되기 때문이다. Scanner로 값을 받고, 연산자로 계산하고, ifswitch로 판단하고, for문으로 반복하는 구조는 이후의 모든 프로그램에서 계속 반복된다.

즉, 둘째 날은 단순히 문법 항목을 많이 외운 날이 아니라 “코드가 스스로 흘러가게 만드는 방법”을 배운 날이었다.

Community

Comments

0 comments

Comments appear immediately. Use report if something needs review.

No comments yet.