▶︎[JAVA]맛보기 포스팅는 유튜브 '생활코딩'의 JAVA1 수업을 수강하며 기록했다.
지금까지 프로그래밍에서 가장 본질적이고 공통적으로 필요한 지식들을 사용해서 문제를 해결해봤다. 지금부터는 배우지 않은 것들을 사용해 볼 것이다.
앞으로 프로그램을 만들면서 생길 불편함과 그걸 극복하도록 도와줄 도구에 대해 알아보자.
double ValueOfSupply = 12345.0;
double vatRate = 0.1;
double expenseRate = 0.3;
double vat = ValueOfSupply*vatRate;
double total = ValueOfSupply + vat;
double expense = ValueOfSupply*expenseRate;
double income = ValueOfSupply - expense;
double dividend1 = income * 0.5;
double dividend2 = income * 0.3;
double dividend3 = income * 0.2;
System.out.println("Value of supply : "+ValueOfSupply);
System.out.println("VAT : "+ vat);
System.out.println("Total : "+ total);
System.out.println("Expense : "+ expense);
System.out.println("Income : "+ income);
System.out.println("Dividend1 : " + dividend1 );
System.out.println("Dividend2 : " + dividend2 );
System.out.println("Dividend3 : " + dividend3 );
위 프로그램에서 만약 수익에 따라 배당률이 달라진다면 어떻게 해야 할까?
-> 수익(income)이 10000원 미만인 경우에는 Dividend1이 전 수익을 가져간다.
if문을 사용하여, 배당률 정의 부분을 다음과 같이 변경했다.
문법: if(조건){실행내용} else{실행내용}
double dividend1;
double dividend2;
double dividend3;
if(income > 10000.0) {
dividend1 = income * 0.5;
dividend2 = income * 0.3;
dividend3 = income * 0.2;
} else {
dividend1 = income * 1;
dividend2 = income * 0;
dividend3 = income * 0;
}
이때 dividend1~3 변수를 if문 밖에서 미리 선언한 이유는 무엇일까?
-> 선언한 변수의 유효 범위는 언제나 {} 블럭 안쪽이기 때문이다.
이처럼 조건문은 어떠한 조건에 따라 작업의 방향이 두개 이상으로 나뉘어질 때 사용한다.
다음은 배당률 변수 선언 코드이다.
double dividend1 = income * 0.5;
double dividend2 = income * 0.3;
double dividend3 = income * 0.2;
이렇게만 보면 0.5, 0.3, 0.2의 의미를 명확히 알 수 없기 때문에, 변수 선언을 해주기로 한다.
double rate1 = 0.5;
double rate2 = 0.3;
double rate3 = 0.2;
double dividend1 = income * rate1;
double dividend2 = income * rate2;
double dividend3 = income * rate3;
[ 위 코드의 문제점과 배열을 사용하는 이유 ]
코딩은 늘 극단적 상황을 예측해야 하는데, 선언된 변수가 많아질수록 변수가 오염될 위험성이 커진다. 또, rate1~3의 변수들이 이름만 비슷하게 생겼을 뿐 서로 같은 성격의 데이터라는 것을 알기 어렵다. 이러한 문제점을 해결하기 위해 배열을 사용한다.
배당률이 변수로 선언되었던 부분을 배열을 사용한 변수로 수정하여 작성했다.
double[] DividendRates = new double[3];
DividendRates[0] = 0.5;
DividendRates[1] = 0.3;
DividendRates[2] = 0.2;
이때, double[] DividendRates = new double[3]; 은
double형 데이터로 이루어진 배열을 DividendRates라는 변수로 선언하고, 그 배열에는 값이 3개 들어갈 수 있다고 정의한 것이다.
[ 배열을 사용하여 나아진 점 ]
1. 각 배당률을 의미하는 부분이 서로 연관된 값이라는 것을 알 수 있게 되었다.
2. 변수가 3개에서 1개로 줄어들어, 오염될 가능성이 줄어들었다.
*코드의 정리가 중요한 이유:
작성하다보면 점점 코드가 복잡해져서, 좋은 프로그램을 만드는 것이 어려워진다.
연관된 데이터를 정리정돈 하는 수단이 '배열'이며, 배열은 반복문과 결합했을 때 폭발적인 효과를 갖게 된다.
다음은 각 동업자의 배당 금액을 출력하는 코드이다.
double[] DividendRates = new double[3];
DividendRates[0] = 0.5;
DividendRates[1] = 0.3;
DividendRates[2] = 0.2;
double dividend1 = income * DividendRates[0];
double dividend2 = income * DividendRates[1];
double dividend3 = income * DividendRates[2];
System.out.println("Dividend1 : " + dividend1 );
System.out.println("Dividend2 : " + dividend2 );
System.out.println("Dividend3 : " + dividend3 );
만약 동업자가 1만명이라면 이러한 코드는 각 1만줄이 반복되어야 할 것이다.
같은 작업을 하지만 그 속의 데이터만 다른, 반복되는 코드를 정리할 때 반복문을 사용한다.
반복문 while의 문법:
while(반복 조건) {실행내용}
반복문을 사용하려면 먼저, 얼마나 반복할지 조건을 설정해야 한다. i라는 변수를 정의해서 설정해보자.
int i = 0;
while(i < DividendRates.length) {}
DividendRates 배열의 값은 3개임으로 즉, i가 3보다 작을 때 {}안의 작업을 실행한다.
그리고 실행내용을 작성한다. 각 동업자의 배당금액을 출력하는 작업이 필요하다.
int i = 0;
while(i < DividendRates.length) {
System.out.println("Dividend"+(i+1)+" : " + income * DividendRates[i] );
i = i + 1;
}
위 코드로 출력될 내용은 동업자 누구에게 얼마의 수익이 배당되는지 이며,
작업이 끝나면 i에 1을 더해 다음 값이 반복되도록 한다.
입력값 10000.0을 설정했다.

성공이다!
메소드는 매우 어렵고 충분한 시간을 들여 공부해야 하는 내용이기 때문에, 문법이 이해되지 않을 수 있다. 또 지금까지 작성한 코드가 간단한 프로그램이기 때문에 메소드의 효과가 크지 않아 보일 수 있다. 절망하지 않기..
메소드(method): 서로 연관된 코드를 그룹핑 한 정리 상자이다.
메소드의 기능을 이해하기 위해 상상력을 발휘해보자.
다음 코드는 VAT를 정의하기 위해 작성했던 계산 코드이다.
double vat = ValueOfSupply*vatRate;
만약 이를 계산하는 부분이 한줄이 아니라 굉장히 길고 복잡한 계산이고,
double vat = getVAT();
그 복잡한 계산을 다른 곳에 숨기고 위와 같이 간단하게 불러올 수 있다면 매우 편리하고 보기 좋을 것이다.
메소드를 통해 이런 일을 해볼 것이다.
메소드 만드는 방법:
1. 메소드로 만들고자 하는 코드 부분을 드래그한 후 우클릭 Refactor - Extract Method 선택
2. 메소드 이름을 설정하고, public 선택 및 Replace 해제 후 OK

double vat = getVAT(ValueOfSupply, vatRate);
vat 변수 정의 코드가 위와 같이 변경되고, 다음과 같은 메소드가 main메소드 아래애 추가된다.
private static double getVAT(double ValueOfSupply, double vatRate) {
return ValueOfSupply*vatRate;
}
이때, getVAT 메소드 괄호 안의 값들은 메소드를 실행할 때 필요한 입력값이다.
이 부분을 getVAT()처럼 깔끔하게 표현하고 싶다면,
괄호 안에 내용을 모두 지우되, main메소드와 getVAT메소드가 공통으로 사용할 수 있는 변수를 사용해야 한다.

이처럼 괄호안에 내용을 지우면, 메소드 안의 변수에 빨간줄이 뜬다.
두 변수가 main메소드 안에서 선언되었기 때문이다.
이를 해결하기 위해서 ValueOfSupply, vatRate 두 변수를 전역변수로 선언해준다.
전역변수로 선언하는 방법은 public static double ValueOfSupply; 라는 코드를
main메소드 밖에 선언하는 것이다.
전역변수 선언 코드를 직접 작성하지 않고 이클립스 기능을 이용하는 방법은?
-> 전역변수로 선언하고자 하는 변수에 우클릭 - Refactor - Convert Local Variable to Field 선택 - Access modifier 'public' 선택 후 OK
이런식으로 메소드로 정의 가능한 부분을 모두 정리하고 필요없는 변수를 삭제하면, 전제 코드는 다음과 같다.
public class AccountingMethodApp {
public static double ValueOfSupply;
public static double vatRate;
public static double expenseRate;
public static void main(String[] args) {
ValueOfSupply = Double.parseDouble(args[0]);
vatRate = 0.1;
expenseRate = 0.3;
print();
}
public static void print() {
System.out.println("Value of supply : "+ValueOfSupply);
System.out.println("VAT : "+ getVAT());
System.out.println("Total : "+ getTotal());
System.out.println("Expense : "+ getExpense());
System.out.println("Income : "+ getIncome());
System.out.println("Dividend1 : " + getDividend1() );
System.out.println("Dividend2 : " + getDividend2() );
System.out.println("Dividend3 : " + getDividend3() );
}
public static double getDividend1() {
return getIncome() * 0.5;
}
public static double getDividend2() {
return getIncome() * 0.3;
}
public static double getDividend3() {
return getIncome() * 0.2;
}
public static double getIncome() {
return ValueOfSupply - getExpense();
}
public static double getExpense() {
return ValueOfSupply*expenseRate;
}
public static double getTotal() {
return ValueOfSupply + getVAT();
}
public static double getVAT() {
return ValueOfSupply*vatRate;
}
}
프로그램의 main메소드의 코드가 매우 단정해진 것과
main메소드에 필요한 기타 연산들을 그 아래에 메소드로 정리해 둔 것을 볼 수 있다.
메소드를 할 줄 안다면 이제 객체(Object)라는 것을 배울 준비가 되었다.
객체는 클래스라는 표현을 쓰기도 한다. (같은 내용은 아님)
클래스는 서로 연관된 변수와 메소드를 그룹핑해서 이름을 붙인 정리 상자이다.
이러한 메소드, 클래스 등이 중요한 이유는 소프트웨어를 만드는데 있어 구조를 결정하기 때문이다.
(뼈대 역할)
무엇이 불편해서 클래스라는 개념이 생겼는지 알아보자.
이클립스 프로그램에서 window - show view - outline 이라는 것을 열면, 지금 작업중인 클래스(프로그램)의 멤버(변수, 메소드)들 목록을 볼 수 있다.

우리가 작업중인 프로그램은 회계와 관련된 클래스인데, 작성한 코드 안에는 회계와 관련된 변수/메소드 외에 회계와 전혀 상관없는 변수/메소드가 존재할 수 있다.
클래스는 연관이 있는 멤버들이 모여 있어야 하는데 말이다.
이 멤버들을 주제별로 정리해주어야 한다.
클래스 생성 방법:
코드 최상단에 클래스를 선언하고, 그 안에 소속될 변수와 메소드 선언을 {}안으로 옮겨준다.
class Accounting{
public static double ValueOfSupply;
public static double vatRate;
public static double expenseRate;
public static void print() {
System.out.println("Value of supply : "+ValueOfSupply);
System.out.println("VAT : "+ getVAT());
System.out.println("Total : "+ getTotal());
System.out.println("Expense : "+ getExpense());
System.out.println("Income : "+ getIncome());
System.out.println("Dividend1 : " + getDividend1() );
System.out.println("Dividend2 : " + getDividend2() );
System.out.println("Dividend3 : " + getDividend3() );
}
public static double getDividend1() {
return getIncome() * 0.5;
}
public static double getDividend2() {
return getIncome() * 0.3;
}
public static double getDividend3() {
return getIncome() * 0.2;
}
public static double getIncome() {
return ValueOfSupply - getExpense();
}
public static double getExpense() {
return ValueOfSupply*expenseRate;
}
public static double getTotal() {
return ValueOfSupply + getVAT();
}
public static double getVAT() {
return ValueOfSupply*vatRate;
}
}
그리고 main메소드에서 Accounting 클래스의 멤버들을 사용할 수 있도록 클래스 경로를 적어준다. Accounting.
public class AccountingClassApp {
public static void main(String[] args) {
Accounting.ValueOfSupply = Double.parseDouble(args[0]);
Accounting.vatRate = 0.1;
Accounting.expenseRate = 0.3;
Accounting.print();
}
}
[ 클래스 정리의 좋은 점 ]
1. 사용된 변수와 메소드들이 어떤 클래스에 속한 멤버인지 소속관계를 명확히 할 수 있다.
2. 다른 주제의 코드들과 섞여도 알아보기 좋다.
3. 흔한 이름의 메소드를 사용해도 클래스가 다르다면 같은 이름의 메소드들과 공존할 수 있게 된다.
-> 이것이 객체지향의 핵심 중 하나인 클래스이다.
실제로는 간단한 코드에 이렇게까지 복잡한 체계를 사용하지도 않고 초심자에게는 효율적이지도 않지만, 중급자가 되면 사용할 일이 많을 것이다.
객체지향의 양대 산맥을 이루는 개념이 클래스와 인스턴스이다.
이번 시간에는 인스턴스를 통해 문제를 해결해보자.
인스턴스(instance): 하나의 클래스를 복제해서 서로 다른 데이터의 값과 서로 같은 메소드를 가진 복제본을 만드는 것이다.
다음 코드는 이전 클래스 수업에서 만들었던 main 메소드의 코드이다.
Accounting.ValueOfSupply = Double.parseDouble(args[0]);
Accounting.vatRate = 0.1;
Accounting.expenseRate = 0.3;
Accounting.print();
공급가/국가(vatRate)/비용이 다른 또 다른 상품이 있다고 가정해보자.
코드를 매번 바꾸지 않기 위해서 각 상품마다 클래스를 만들어주고 싶다.
인스턴스 선언 방법:
Accounting 클래스 형식만 들어갈 수 있는 a1변수에 Accounting 클래스 복제본을 대입하고, 클래스 경로를 변수 a1으로 변경한다.
Accounting a1 = new Accounting();
a1.ValueOfSupply = 10000.0;
a1.vatRate = 0.1;
a1.expenseRate = 0.3;
a1.print();
Accounting a2 = new Accounting();
a2.ValueOfSupply = 20000.0;
a2.vatRate = 0.03;
a2.expenseRate = 0.2;
a2.print();
이때, 인스턴스를 선언하여 클래스를 사용하고 싶다면 사용하려는 클래스의 모든 static(정적)을 지워준다.
▶︎ 참고 링크
JAVA에서는 메소드로 구조를 잡고, 메소드와 변수를 그룹핑해서 클래스로 구조를 잡고,
그 클래스를 복제한 인스턴스를 통해 또 다른 구조를 만드는 방식으로 코드의 성을 쌓는다.
이것이 JAVA라는 컴퓨터 언어의 독특한 특징이다.
또 이러한 특징을 현대의 많은 언어들이 따르고 있다.
JAVA1 수업 끝!!
지금까지 배운 것 만으로 나만의 간단한 문제들을 자바를 통해 해결하다보면
해결할 수 없는 문제(불편함)를 반드시 만나 해결하지 않고는 견딜 수 없는 상태가 온다.
그 때가 바로 더 공부해 나갈 때이다. 너무 성급하게 더 많은 걸 배우려고 하지 않아도 된다.
-이고잉-
느낀 점
재밌다 너무 재밌다.. 간단한 예제를 통해서 한 실습이라서 재밌었던 걸까.
정리에 정리를 거듭하고 모든 간단한 코드처럼 보여도 유기적으로 모두 연결되어 있다니,
객체지향이라는 게 엄청 매력적이고 재밌게 느껴진다.
지금까지 선생님이 강조하신 부분은 스스로 문제(불편함)를 느끼고, 해결방법을 찾는 것이었다.
나는 해결방법을 찾는 것보다 문제를 찾는 것이 더 어렵게 느껴진다.
실제 나의 문제가 아니라서 그랬을까? 직접 나의 프로그램을 만들다보면 문제점이 잘 느껴질까.. 아직 자바문법에 익숙하지 않아서 개선 가능성을 찾지 못하는 것 같다.
얼른 내 애플리케이션을 기획해서 만들어보고 싶은 생각이다. 빠른 시일 내에 실천해보자..!