본문 바로가기

자바 프로그래밍/1장. 자바 프로그래밍이란

3. 자바 컴파일러를 이용해 컴퓨터 프로그램 만들기

반응형

자바 컴파일러가 준비되었으니 그것을 이용하는 방법을 알아볼 차례이다. 먼저 번역에 사용할 소스 코드 파일을 준비하도록 하자.

3-1 소스코드 준비하기

메모장을 열고 다음 코드를 작성하고 저장한다.

kor1 = 50
kor2 = 60
kor3 = 80

total = kor1 + kor2 + kor3
avg = total / 3

 

필자의 경우는 다음 그림처럼 폴더(Workspace)를 생성하고 그 안에 Program.java 로 저정하였다. 이 때 반드시 파일명의 첫 글자는 대문자로 하고 확장자는 .java로 한다.

 

 

확장자를 .java로 하는 이유는 자바 컴파일러가 자신과 관련된 파일인지를 확인할 수 있게 방법이기 때문이다.

 

위의 코드를 작성할 때는 코드를 그대로 보고 따라치기

 보다는 다음처럼 위의 내용을 숙지하고 코드를 안보고 쳐보려고 노력하는 것이 필요하다.

// 국어성적 3개를 초기화

// 총점구하기

// 평균구하기

 

위와 같이 작성할 내용을 간단히 설정하고 코드를 직접 채워나가는 것이다.

// 국어성적 3개를 초기화
kor1 = 30; 
kor2 = 50; 
kor3 = 60;

// 총점구하기
total = kor1 + kor2 + kor3;

// 평균구하기
avg = total / 3;

 

물론 코드를 작성하다가 긴가민가 하는 부분은 위의 코드를 참고할 수 있다. 이렇게 안보고 직접 작성하려는 노력이 있어야만 스스로 코드를 작성할 수 있는 시기를 앞당길 수 있을 것이다.

3-2 번역하기

위에서 작성한 Program.java 파일을 컴파일하기 위해 명령 프롬프트 창을 열고 cd 명령어로 소스 파일이 있는 Workspace 경로로 현재 위치를 변경하도록 하자.

 

C:\Users\newlec>cd c:\Workspace

c:\Workspace>

 

디렉토리를 변경한 후에 현재 디렉토리에 컴파일 할 소스 파일이 있는지 dir 명령으로 목록을 조회해보도록 하자.

 

c:\Workspace>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: F215-9B86

 c:\Workspace 디렉터리

2021-07-22  오전 11:45    <DIR>          .
2021-07-22  오전 11:45    <DIR>          ..
2021-07-22  오전 11:23               490 Program.java
               1개 파일                 490 바이트
               2개 디렉터리  521,068,228,608 바이트 남음

c:\Workspace>

 

Program.java 파일이 있는 것을 확인 하였다면 이제 자바 컴파일러(javac.exe)를 이용해서 번역을 해보자.

 

c:\Workspace>javac Program.java
Program.java:1: error: class, interface, enum, or record expected
int kor1, kor2, kor3;
^
Program.java:2: error: class, interface, enum, or record expected
int total;
^
Program.java:3: error: class, interface, enum, or record expected
float avg;
^
Program.java:6: error: class, interface, enum, or record expected
kor1 = 30;
^
Program.java:7: error: class, interface, enum, or record expected
kor2 = 50;
^
Program.java:8: error: class, interface, enum, or record expected
kor3 = 60;
^
Program.java:11: error: class, interface, enum, or record expected
total = kor1 + kor2 + kor3;
^
Program.java:13: error: class, interface, enum, or record expected
avg = total / 3;
^
8 errors

 

컴파일러는 번역 결과로써 위와 같은 메시지를 출력하고 있다. 이제 이 오류가 무엇을 의미하는지 그리고 그것을 어떻게 하면 해결할 수 있는지를 알아보도록 하자.

 

3-3 자바 실행 코드는 반드시 두 개의 블록을 가진다.

자바는 연산식을 사용할 때 반드시 두 개의 블록을 포함하는 구조를 가지고 있다. 제일 바깥쪽 블록은 클래스 블록 안쪽 블록은 함수 블록이다.

 

 

그 두가지 블록을 모두 포함한 코드는 다음과 같다.

 

public class Program{
    public static void main(String[] args){

        // 국어성적 3개를 초기화
        kor1 = 30
        kor2 = 50
        kor3 = 60

        // 총점구하기
        total = kor1 + kor2 + kor3

        // 평균구하기
        avg = total / 3

    }
}

 

그럼 이 두가지 블록을 포함하는 이유는 무엇인지는 뒤에서 다룰 내용이므로 여기서 모두 얘기할 수는 없지만 간단하게라도 알아보도록 하자. 

 

A) 함수 블록

 

두 개 블록 중에서 안쪽 블록에 해당하는 블록은 함수 블록이다. 





함수 블록은 코드가 길어지면서 그것을 나누어 작성하고자 하는 필요성으로 함수라는 블록을 만들어서 그것으로 코드를 나누는 방법을 사용하게 되었다.

 

함수를 이용해서 코드를 나누지 않는다면 그것은 마치 한 장으로 만들어진 소설책(두루마리 화장지처럼 말린)을 연상케할 것이다.

 

한줄로 이루어진 코드를 함수로 나누면 다음과 같은 그림이 만들어진다.




함수로 나누어진 코드는 서로 분리가 되어 있기 때문에 그것을 엮는 함수가 필요한데, 그 함수를 main 함수라고 한다. main 함수는 프로그램의 시작을 담당하는 함수로써 반드시 프로그램에 포함되어 있어야하며 하나만 있어야 한다. 

 

public static void main(String[] args){
      // 제일 먼저 실행될 코드를 작성하는 영역

}
 

 함수를 배우는 단원이 될 때까지는 항상 위와 같은 main 함수를 작성하고 그 안에 코드를 작성하도록 하다.

 

B) 클래스 블록

 

함수로 프로그램을 나누어 만드는 것으로 만족하던 시기가 있었지만 프로그램의 크기가 더욱 커지면서 또 다른 고민거리가 생기게 되었다.

 

다음 처럼 함수의 개체수가 너무 많아지면 그것을 정리할 방법이 딱히 없었다는 것이다. 

 

그래서 처음부터 코드를 나누는 방법을 완전히 다른 시각에서 바라보게 되었는데, 그것이 바로 객체지향이다. 객체지향은 프로그램의 세상을 현실세계에 빗대어 작성하는 방법으로써 현실세계에는 객체가 있고 그 객체가 행위를 하는 것처럼  프로그램에도 객체가 있고 객체가 행위(함수)를 가지고 있다고 보는 방법론이다.

 

따라서 자바에서 함수는 무조건 객체를 정의하는 class 블록에 포함하도록 강요하고 있다.

 

객체지향에 대한 자세한 내용은 뒤에서 다루기로 하고 여기서는 일단 모든 함수는 클래스에 포함된다고만 생각하고 코드를 다음처럼 작성하도록 하자.

 

public class Program{
    public static void main(String[] args){

        // 국어성적 3개를 초기화
        kor1 = 30
        kor2 = 50
        kor3 = 60

        // 총점구하기
        total = kor1 + kor2 + kor3

        // 평균구하기
        avg = total / 3

    }
}
 

 

당분간은 위의 코드처럼 class와 그 안에 main 함수를 포함하는 구조로 코드를 작성하게 될 것이다.

 

이제 컴파일 과정에서 발생한 문제를 해결되었는지 다시 컴파일을 해보도록 하자.

3-4 문장 구분자

코드를 다시 컴파일하면 그 결과는 다음처럼 또 다른 오류 메시지를 보여준다.

 

c:\Workspace>javac Program
Error: Could not find or load main class Program
Caused by: java.lang.ClassNotFoundException: Program

c:\Workspace>javac Program.java
Program.java:5: error: ';' expected
                kor1 = 30
                         ^
Program.java:6: error: ';' expected
                kor2 = 50
                         ^
Program.java:7: error: ';' expected
                kor3 = 60
                         ^
Program.java:10: error: ';' expected
                total = kor1 + kor2 + kor3
                                          ^
Program.java:13: error: ';' expected
                avg = total / 3
                               ^
5 errors

 

이 메시지에서 첫 번째 오류는 다음과 같다.

 

Program.java:5: error: ';' expected 

 

이 의미는 Program.java 파일의 5번째 줄에서 세미콜론(;)이 있어야 하는데 없다(expected)라는 말이다. 그리고 그것이 있어야 할 위치도 어디인지도 정확하게 알려준다.

 

                kor1 = 30

                               ^

 

^ 이 기호가 가리키는 위치에 세미콜론을 입력하지 않았다는 말하는 것이다. 정말 정확하게 오류를 알려주고 있는 않는가?

 

그렇다면 세미콜론(;)이  무엇이고 왜 그 위치에 필요한지를 알아보자.

 

자바에서 세미콜론(;)은 문장을 구분하는 구분기호이다 수학에서는 문장을 구분할 때 내려쓰기가 구분기호로 사용된다.

 

kor1 = 30
kor2 = 50
kor3 = 60

 

따라서 위와 같이 내려쓰기가 되면 그것으로써 수학에서는 위의 문장이 3개라는 것을 알 수 있게 한다.

 

하지만 자바에서는 반드시 세미콜론(;)을 사용함으로써 줌으로써 문장을 구분하도록 하고 있다.

kor1 = 30;
kor2 = 50;
kor3 = 60;

 

이렇게 문장이 끝날 때마다 세미콜론을 적어 주어야만 한다. 어떤 사람들은 이것이 불편하다고 말하는 경우도 있지만 장점이 되기도 한다.

 

구분자가 있기 때문에 반드시 내려쓰지 않고 필요에 따라서는 한 줄에 여러 문장을 작성할 수도 있다.

 

kor1 = 30; kor2 = 50; kor3 = 60;

 

이것은 구분자를 따로 가지고 있기 때문에 가질 수 있는 특징이다. 이것은 코드를 유연하게 만드는 장점이 되기도 한다.

 

이제 다음처럼 문장마다 세미콜론을 적어서 오류를 수정하고 다시 컴파일 해보도록 하자.

 

public class Program{
    public static void main(String[] args){

        // 국어성적 3개를 초기화
        kor1 = 30;
        kor2 = 50;
        kor3 = 60;

        // 총점구하기
        total = kor1 + kor2 + kor3;

        // 평균구하기
        avg = total / 3;

    }
}

3-5 키워드 선언하기

코드를 다시 컴파일하면 다음처럼 또 다른 오류 메시지를 보여준다.

 

c:\Workspace>javac Program.java
Program.java:5: error: cannot find symbol
                kor1 = 30;
                ^
  symbol:   variable kor1
  location: class Program
Program.java:6: error: cannot find symbol
                kor2 = 50;
                ^
  symbol:   variable kor2
  location: class Program
Program.java:7: error: cannot find symbol
                kor3 = 60;
                ^
 ...
9 errors

 

이 메시지에서 첫 번째 오류는 다음과 같다.

 

Program.java:5: error: cannot find symbol

                kor1 = 30;

                ^

 

이 의미는 Program.java 파일의 5번째 줄에서 사용된 심볼을 찾을 수 없다는 오류이다. 여기서 심볼이란 식별할 수 있는 키워드를 말한다.

 

kor1 = 30;

^

 

쉽게 말해서 위에서 지적한 이 키워드가 무엇인지 식별할 수 없다는 말이다.

 

코드에서 사용되는 모든 키워드는 컴파일러가 식별할 수 있는 것이어야만 한다. 

class, public, void, String, 등은 모두 컴파일러가 이미 인지하고 있는 키워드이다. 

 

하지만 우리가 사용하고 변수명들은 컴파일러가 인식하지 못하고 있기 때문에 오류가 발생한 것이다.

 

이런 문제를 해결하기 위해서 우리는 코드에서 사용할 모든 기호에 대해서 다음처럼 기호를 선언하고 사용해야만 한다.

 

 



위와 같이 kor1, kor2 등을 코드에서 변수로 사용할 것임을 선언하면 컴파일러는 그 키워드들이 변수임을 인식한다.

 

사실 오래전에는 변수를 위와 같이 인식만 시키면 되었지만 최근의 언어들은 다음처럼 좀 더 구체적으로 변수를 선언한다.

 

 

 

변수명 앞에 변수에 담을 값의 형식명칭을 이용해서 변수를 선언해야만 한다. 그렇게 함으로써 변수에 다른형식의 값이 담겨지는 것을 컴파일 과정에서 막을 수가 있다.

 

int 는 자바에서 32비트 크기의 정수를 의미하는 값의 형식명칭이고, float은 32비트 크기의 실수를 의미하는 값의 형식명칭이다.

 

자바에서는 8비트 정수 byte, 16비트 정수 short, 32비트 정수 int, 64비트 정수 long 등 다양한 값의 형식이 있는데, 그것에  대해서는 뒤에서 다루게 되므로 여기서는 간단히 정수와 실수를 의미하는 int와 float이 있다는 정도로만 이해하도록 하자.

 

3-6 주석

코드에는 설명(=주석)을 붙이고 그것을 컴파일러가 해석하지 않게 할 수 있다. 다음처럼 이중 슬래시(//)를 사용하면 컴파일러는 그 줄에서 이후 문자를 해석하지 않는다.

 

 

 

코드에 주석을 추가하는 방법은 다음 두 가지 이다.

 

한줄 주석 : //  주석내용

범위 주석 : /* 주석내용 */

 

범위 주석은 다음처럼 여러 줄을 주석으로 만들기 위해 사용할 수도 있고

 

 

 

다음처럼 코드 중간에 주석을 포함하고 싶을 때도 사용할 수

3-7 컴파일한 결과물 실행하기

이제 지금까지 작성한 코드를 다시 컴파일하면 더 이상 오류는 발생하지 않고 다음처럼 프롬프트만 깜빡인다.

 

c:\Workspace>javac Program.java

c:\Workspace>_

 

그것은 컴파일이 성공했다는 의미이다.

 

컴파일의 결과물을 확인하기 위해서 dir 명령을 이용해서 목록을 확인해보자.

 

c:\Workspace>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: F215-9B86

 c:\Workspace 디렉터리

2021-07-23  오전 01:18    <DIR>          .
2021-07-23  오전 01:18    <DIR>          ..
2021-07-23  오전 01:18               302 Program.class
2021-07-23  오전 01:18               310 Program.java
               2개 파일               502 바이트
               2개 디렉터리  521,043,132,416 바이트 남음

 

Program.class 파일이 새로 생긴 것을 확인할 수 있다. 이 파일은 Program.java 파일이 컴파일된 결과물이다.

 

그런데 이 결과물은 CPU가 바로 실행할 수 있는 실행파일은 아니다. 컴파일을 절반만한 중간코드이다.

 

전체를 번역하지 않은 이유는 자바의 탄생 배경과 관련되어 있기 때문에 자세한 내용은 다음 단원에서 알아보기로 하자.

 

자바 실행코드를 실행하려면 CPU가 바로 실행할 수 없기 때문에 다음처럼 실행을 도와주는 java.exe를 이용해서 실행해야만 한다.

 

c:\Workspace>java Program

 

 java.exe 파일은 중간까지만 컴파일된 코드의 나머지를 컴파일하는 기능을 가지고 있으며 클래스를 로드하는 기능, 메모리 관리 기능 내포 등을 내포하고 있다. 그래서 이 파일(java.exe)을 자바 실행환경이라고도 하고 자바 가상머신이라고도 한다.

 

3-8 총점과 평균 값을 출력하기

c:\Workspace>java Program 으로 실행된 결과는 다음처럼 아무런 결과를 출력하지 않는 다소 허무한 결과를 보여준다.

 

c:\Workspace>java Program

c:\Workspace>

 

소스 파일에 출력하는 코드가 없으니 그것은 당연한 결과이다. 그래서 이번에는 출력을 위한 코드를 추가해보기로 하자. 

 

출력을 위한 코드는 자바 언어에 포함되어 있지 않다. 출력을 위한 코드는 API라는 이름으로 별도로 제공된다.

 

 

앞서서 우리는 프로그램은 위와 같은 구성을 갖는다고 하였다. 실제 구동을 담당하는 “실행 절차”와 그 절차를 나누는 “모듈화”는 프로그래밍 언어에 포함된 기능이지만 “입/출력”을 담당하는 부분은 언어가 제공할 수 없는 기능이다.

 

A) 입/출력 플랫폼

 

언어의 기능은 CPU와 MEMORY의 기능을 대신한다. 그 외의 다른 장치의 기능은 언어에 포함되어 있지 않다.

 

하드디스크나 모니터, 키보드 등과 같은 입/출력 장치들은 언어에 포함될 수가 없다. 그 이유는 입/출력 장치는 종류도 많고 자주 바뀔 수 있기 때문이다.

 

그 이유를 알기 위해서 간단한 프로그램을 만들어보자.

 

 

 

입력 장치로부터 값을 받아서 계산하고 그것을 출력 장치로 출력하는 코드를 가정해보자.



 

 

그러다가 위의 그림처럼 A장치를 B장치로 변경하는 일이 발생하면 어떻게 해야 하는가. 어쩔 수 없이 A장치를 이용하는 입/출력 코드를 B장치를 이용하는 코드로 변경해야만 할 것이다.

 

문제는 그렇게 수정해야 할 코드의 수가 한 두개가 아니면 어떻게 해야 하는가?

 

 

 

일반적으로 컴퓨터에는 수십 또는 수백개의 프로그램이 존재하는 것을 생각했을 때 수정해야 할 코드의 량이 많다는 것은 과장된 말은 아닐 것이다.

 

그렇다면 입/출력 장치가 달라져도 프로그램을 수정하지 않을 수 있는 방법은 없을까? 그래서 생각한 것이 인터페이스라는 개념이다.

 

B) 코드 분리와 인터페이스

 

프로그램에서 뿐만 아니라 현실 세계에서도 무엇을 분리하고 다시 조립하려면 중간에 그것을 이어주는 도구가 필요한데, 우리는 그것을 인터페이스라고 한다.

 

쉬운 예로 핸드폰의 배터리나 카메라 렌즈를 생각해 볼 수 있는데 배터리를 바꿔끼우거나 렌즈를 바꿔끼우는 것에는 일종의 약속과 그 약속에 준한 점합점(인터페이스)이 존재한다.

 

그리고 인터페이스가 있음으로 인터페이스만 일치한다면 배터리나 렌즈를 만든 회사를 가리지 않고 다른 제품으로 바꾸어서 사용할 수도 있다.

 

“인터페이스를 사용한다면 그 부품이 바뀌어도 제품에는 영향을 주지 않는다.”

 

프로그래밍에서도 입/출력 장치를 인터페이스를 이용하는 방법을 고려해볼 수 있다.

 

 



장치를 직접 사용하는 것을 차단하고 일정한 약속을 기반으로 장치를 바꿔서 사용할 수 있도록 해야 한다.

그런데 그것을 가능하게 하려면 자바 코드 안에서 인터페이스 역할을 할 수 있는 구현체가 필요한데, 그런 것이 있을까?



C) 함수와 인터페이스의 관계

 

수학에서의 함수는 프로그래밍에서 인터페이스 역할을 할 수 있는 아주 훌륭한 능력을 가지고 있다.

 

 

 

위의 그림은 수학에서 함수를 표현하는 방법들을 보여주고 있다. 그래서 그런지 왠지 함수는 어려운 느낌이 들지만 사실 함수는 쉽고 단순한 개념이다.

 

 

 

위와 같은 복잡해 보이는 수학식이 있다고 가정하자. 이 식이 복잡해 보이는 이유는 반복되는 식이 여러번 포함되어 있기 때문이다.

 

 

 

이식을 간결하게 만들려면 반복되는 식을 쉬운 이름으로 치환하는 방법을 사용할 수 있다.

 

반복되는 식을 y라고 정의하고 그것을 이용해 연산식을 다시 작성하면 연산이 많이 간결해진다.

 

이런 목적으로 사용되는 단순한 기호(y)를 우리는 함수라고 한다. 함수는 위와 같이 단순히 기호 하나로 정의할 수도 있지만 인자(x)가지는 경우는 다음처럼 함수명을 정의하기도 한다.

 

 

하지만 연산의 의미를 살려서 다음처럼 정의하는 것이 가장 일반적인 함수명이다.

 

 

x 인자는 질량이라고 가정하고 그 값을 함수에 넘기면 그에 따른 힘을 얻어준다면 그 함수 이름은 power라고 하는 것이 바람직하다.

 

함수는 단순하면서도 굉장한 능력들을 가지고 있다.

 

  • 반복되는 코드를 하나의 함수로 집중화 하는 능력
  • 긴 연산식 일부분을 함수로 잘라내는 능력
  • 함수 내부의 변화가 그것을 사용하는 연산식에 영향을 주지 않게 차단하는 능력

 

이 외에도 다양한 능력을 가지고 있지만 세 번째 능력은 함수를 인터페이스로 사용하기에 아주 적합한 능력이다.

 

 

 

위의 그림처럼 장치를 동작시키는 코드(driver)는 함수로 정의하고 어플리케이션에서는 함수명을 사용하는 방법을 사용함으로써 장치가 바뀌어도 driver만 바꿀 뿐 어플리케이션은 영향을 받지 않는다.

 

 

 

이렇게 장치를 활용하기 위한 함수들은 운영체제가 제공하며, 그것을 OS가 제공하는 API(Application Programming Interface)라고 한다. 그러나 자바는 운영체제가 제공하는 API를 사용하지는 않고 자바 플랫폼이 제공하는 자바 API만을 사용한다.

 

그 이유는 WORA(Write Once Run Anywhere)를 지원하기 위함이며 그것에 대해서는 다음 단원에서 자세히 알아보도록 하자.

 

D) 자바 콘솔 API를 이용해서 총점과 평균 출력하기

 

자바가 제공하는 API를 이용해서 콘솔에 값을 출력하는 방법은 다음과 같다.

 

public class Program{
    public static void main(String[] args){
        …((생략)

        // 총점구하기
        total = kor1 + kor2 + kor3;

        // 평균구하기
        avg = /* 중간에 들어간 주석 */ total / 3;

        System.out.printf("total is %d\n", total);
        System.out.printf("avg is %f\n", avg); 
    }
}

 

자바 플랫폼이 제공하는 printf 함수를 이용하면 화면에 총점과 평균이 출력할 수 있다. 위의 코드를 저장하고 다시 컴파일 한 후에 실행해보도록 하자. 그러면 다음과 같은 결과를 볼 수 있을 것이다.



c:\Workspace>java Program
total is 140
avg is 46.000000




반응형

'자바 프로그래밍 > 1장. 자바 프로그래밍이란' 카테고리의 다른 글

1. 자바 프로그램이란  (1) 2023.11.18
학습내용  (1) 2023.11.18
4. 자바 언어의 특징  (0) 2022.01.23
2. 자바 개발환경 설치하기  (0) 2022.01.23
1. 자바 프로그래밍  (0) 2022.01.23