JAVA의 개발 환경
자바는 C, C++ 과 달리 JVM(Java Virtual Machine) 이라는 가상머신이 존재하고, 이를 실행하기 위한 환경이 JRE(Java Runtime Environment) 이다. 그리고 우리는 JDK(Java Development Kit) 라는 것도 들어본 적이 있다. 도대체 이 3개의 차이는 무엇일까?
JRE(Java Runtime Environment)
가장 먼저 JRE에 대해서 살펴보자. JRE는 Java Runtime Environment의 약자로 자바로 만들어진 프로그램을 실행시키는데 필요한 라이브러리들과 각종 API, JVM(Java Virtual Machine)이 포함되어 있는 자바로 만들어진 프로그램을 실행시키기 위한 도구이다.
JVM(Java Virtual Machine)
JVM은 Java Virtual Machine의 약자이고, 용어 그대로 자바를 실행하기 위한 가상 기계를 뜻한다. 그리고 실제 의미도 크게 다르지 않다. 자바는 JVM을 가짐으로써 OS에 종속되지 않는다는 엄청난 장점을 가지고 있다. 각 운영체제 별로 명령어를 해석하는 방식이 다르기 때문에 다르게 운영체제가 관리하는 C, C++ 같은 경우에는 OS에 종속되어 있지만, JVM이라는 프로그램 위에서 돌아가는 Java는 OS에 종속받지않고 CPU가 JAVA를 인식하고 실행 할 수 있게 도와주는 도구이다.
자세한 내용은 [JVM이란] 에서 확인이 가능합니다.
JDK(Java Development Kit)
JDK는 자바 개발 키트(Java Development Kit)의 약자로 이름 그대로 개발자들이 자바를 사용하여 개발을 하기위해 사용되는 도구이다. JDK안에는 개발하는데 필요한 라이브러리들과 javac, javadoc 등의 개발 도구들이 포함되어 있고 실행도 해주어야하기 때문에, JRE도 같이 포함이 되어있다. 그런데 우리는 실질적으로 사용을 하면서 Oracle JDK, Open JDK 라는 것을 보게 된다. 둘의 차이는 무엇일까?
Oracle JDK VS Open JDK
2010년에 Oracle이 Sun MicroSystem을 인수한 이후로 JDK는 유료화가 되었다. Java 6 시절에는 Open JDK가 Oracle JDK에 비해 성능이나 안정성이 크게 떨어지는 문제가 있었으나 현재에 와서는 Android Studio or Intellij IDEA와 같은 통합 개발 환경에서 JDK를 사용하는 상위 회사들이 Open JDK 기반 JetBrains로 빌드를 전환하면서 Open JDK를 선호하는 방향으로 가고 있고, 성능 상의 차이도 크지 않다고 한다. 추가적으로 Oracle은 3년 마다 릴리스를 제공하고 OpenJDK는 6개월마다 릴리스하기 때문에, 6개월마다 Open JDK의 업데이트를 해주어야 한다는 단점이 있다.
m1이 출시된지 얼마 안 됐을 때는 Oracle 에서 제공하는 OpenJDK와 가장 유사한 AdoptOpenJDK 가 없어서 azul에서 제공하는 zulu jdk를 많이 사용하였으나 이제는 brew를 통해서 arm 이 지원되는 AdoptOpenJDK가 사용이 가능하므로 이를 권장합니다!
터미널에서도 위와 같은 도구를 이용하여 javac 를 통해 java 파일을 .class 파일로 컴파일을 하고, java 명령어로 실행이 가능하지만 이 부분은 너무나도 번거로운 문제가 존재했다. 그래서 이를 보완하기 위해 자동으로 빌드도 해주고 실행도 해주는 도구들이 존재했으면 좋겠다! 라는 생각으로 등장한 것이 Build Tool 입니다.
Build Tool
빌드 도구(Build Tool) 이란, 소스 코드를 컴파일, 테스트, 정적분석 등을 실행 하여 실행가능한 애플리케이션으로 자동 생성하는 프로그램을 말한다.
Build Tool 종류
빌드 도구의 종류에는 크게 Ant, Maven, Gradle이 있다.
1. Ant
특징
- XML 기반으로 빌드 스크립트를 개발합니다.
- 규칙이 없습니다.
- 절차적입니다. 따라서 명확한 빌드 절차 정의가 필요합니다.
- 생명주기를 갖지 않아서 각각의 Target에 대한 의존관계와 작업을 직접 정의해주어야 합니다.
단점
- 유연성이 높으나 프로젝트가 복잡해지는 경우 Build 과정을 이해 할 수가 없습니다.
- XML, Remote Repository를 가져올 수가 없습니다.
- 결정적으로 스크립트의 재사용이 어렵습니다.
2. Maven
메이븐은 Ant의 단점을 해결하기 위해 등장하게 되었습니다.
특징
- 프로젝트에 필요한 모든 종속성을 리스트의 형태로 Maven에게 알려서 종속성을 관리합니다.
- XML, Repository를 가져올 수 있습니다!
그럼에도 불구하고 Maven에도 단점은 존재합니다.
단점
- 라이브러리가 서로 종속할 경우 XML이 복잡해집니다.
- 계층적인 데이터를 표현하기에는 좋지만, 플로우나 조건부 상황을 표현하기 어렵습니다.
- 맞춤화된 로직 실행이 어렵습니다.
마지막으로 이 둘의 단점을 보완한 Gradle이라는 빌드 도구가 등장했습니다.
3. Gradle
특징
- JVM 기반의 빌드 도구 입니다.
- Ant와 Maven의 단점을 보완하였습니다.
- 오픈소스 기반의 Build 자동화 도구입니다.
- Groovy 기반 DSL로 작성합니다.
장점
- 프로젝트 시작시 설정에 드는 시간을 절약할 수 있습니다.
자주 사용하는 Gradle에 보면 plugin 블럭에 'application' 이 보이게 됩니다. plugin 블럭에 속한 id는 어떠한 task 들의 집합이고, 기본적으로 기존에 수행하던 자바를 빌드하는 task들이 포함되어 있습니다.
이러한 빌드 도구로 인하여 자바를 컴파일하고 프로그램을 실행하거나 테스트 하는 과정이 굉장히 간편해졌습니다. 하지만 코드에 대한 생산성이 떨어져서 통합 개발 환경(Integrated Development Environment, IDE)이 등장하게 되었습니다.
IDE
IDE는 코딩, 디버그, 컴파일 배포 등 프로그램 개발에 관련된 모든 작업을 하나의 프로그램 안에서 처리하는 환경을 제공하는 소프트웨어이다. 최근의 IDE는 GUI도 편리하게 제공해주어 개발자들의 개발 생산성을 크게 높여주었다.
인텔리제이의 유용한 단축키 for Mac
명령어 | 설명 |
Option + Enter | 컴파일 에러 발생 혹은 클래스가 Import 되어있지 않을 경우 많은 부분을 해결해주는 단축키이다. 경고 혹은 컴파일 에러가 발생하면 Option + Enter 부터 눌러보자. |
command + 1 | 프로젝트 탭으로 이동한다. |
command + N | 새 파일을 생성한다. |
shift + shift | 전체 파일 이름을 검색할 수 있다. |
option + UP/DOWN | 문자열 "A B C" 가 주어졌을 때 더블클릭으로 해당 문자열을 전부 바꿀 수 없다. 해당 단축키로 블럭 사이즈를 키우거나 줄일 수 있다. |
command + '/' | 주석을 생성한다. 드래그를 하여 많은 줄의 주석 생성을 한 번에 가능하게 만들어준다. |
command + option + L | 코드의 indent를 이쁘게 만들어주는 단축키이다. |
ctrl + T | Refactoring과 관련된 명령어들의 모임이다. |
shift + command + A | 명령어가 기억이 나지 않을 때에는 해당 명령어를 사용하여 필요한 명령어 검색이 가능하다. |
Intellij 회사인 JetBrain 에서는 다음과 같이 명령어 들의 집합을 소개해주고 있다.
https://www.jetbrains.com/help/idea/reference-keymap-mac-default.html#caret_navigation
궁금한 명령어가 존재한다면 해당 링크에 들어가서 명령어를 확인해보자!
초보 개발자가 알면 좋은 정보
Coding Convention
팀이나 회사 혹은 개발 그룹에서 정해서 사용을 한다. 만약에 정하지 않았을 경우에는 일반적인 언어 컨벤션을 따라야 한다.
위에서 얘기했듯이 기본 컨벤션이 언어마다 다르기 때문에 각 언어에 대한 컨벤션을 참고해서 사용해야 한다.
https://google.github.io/styleguide/javaguide.html
String
String은 Java 에서 제공하는 특별한 자료형이다. String을 생성하는 방법은 2가지 방법으로 생성 할 수 있다.
String a = "string"; // 1. Literal
String abc = new String("abc"); // 2. new 키워드 사용
new 키워드 형태의 String 생성은 우리가 기존에 알고 있던 객체 생성 방식과 비슷하다. 새로이 객체를 생성할 때마다 새로운 메모리가 할당되는 방식이다. 그렇다면 Literal 방식은 기존에 우리가 알고 있던 Primitive Type 처럼 값이 복사가 되거나 변경이 되는 것일까?
아쉽게도 그렇지 않다. 다음과 같은 예시를 살펴보자.
String value = "";
for(int i = 1; i <= 9; i++){
value += i;
}
다음과 같은 코드의 결과로 value를 찍어보면 123456789 가 결과값으로 도출 될 것이라는 것을 어렵지 않게 파악 할 수 있다.
하지만 여기서 중요한 부분은 value 가 저장된 메모리에서 단순히 값이 1, 12, 123 으로 변경된 것이 아니다.
실은 value += i; 를 통해 값이 변경 될 때마다 값에 대한 메모리 주소를 새로이 할당 하게 된다. 즉, 위의 코드에서는 총 9번이라는 새로운 객체의 생성이 이루어진 것이다. 즉, Java 에서의 String은 Immutable 이라고 할 수 있다.
왜 자바는 String을 Immutable(불변)하도록 만든 것 일까? 대표적으로 3가지 이유가 존재한다.
캐싱, 동기화, 보안성 이다.
캐싱
먼저 캐싱에 관한 이야기이다. 사실 String의 리터럴은 Constant Pool 즉, 상수 풀을 이용하게 된다. 리터럴 방식으로 문자열을 생성하면 상수 풀에 등록이 된다. 이후에 동일한 문자열이 상수풀에 들어있으면 새로이 생성하는 것이 아닌 상수 풀에서 해당 문자열의 주소를 새로이 선언한 Literal 타입에 대입하는 것이다. 다음과 같은 예시를 살펴보자.
String a1 = "abc";
String a2 = "abc";
System.out.print(a1 == a2);
다음과 같은 상황에서 객체 타입이라는 것을 새로이 배운 사람이라면 음 객체 타입을 새로 생성한 것이라고 봐야하고? 자바는 새로 생성할 때마다 다른 메모리 주소가 할당이 되니까 false가 나와야겠다! 라는 생각으로 도출 할 것이다. 그리고 이러한 사고방식은 옳게 된 사고 방식이다. 그러나 String의 Literal 타입만은 예외이다. 위에서 Constant Pool 을 사용한다는 것을 기억하자. "abc"라는 문자열을 a1 변수가 생성하게 되면서 Constant Pool의 메모리 공간에 "abc"가 저장이 된다. 그리고 a2는 동일한 문자열이 Constant Pool에 존재함을 확인하고 같은 메모리 공간을 공유하게 된다. 결론적으로 a1 == a2 는 동일성이 일치하는 true를 반환하게 된다.
Constant Pool에 등록된 리터럴은 GC의 대상인가?
위와 같은 질문이 떠오를 수 있다. JDK 7 부터는 Constant Pool이 힙 영역으로 들어가게 되었다. 기존의 메서드 영역이 Heap의 Metaspace 영역에 포함되면서 같이 GC의 대상으로 포함되었다. 따라서 사용되지 않는 문자 리터럴에 대한 메모리 낭비에 대해서도 고민하지 않아도 되게 되었다.
추가적으로 그럼 new 키워드로 생성한 String도 동일한가? 라는 생각이 들 수 있다. 하지만 new 키워드는 기존의 객체 생성과 동일하다.
String a1 = new String("abc");
String a2 = new String("abc");
System.out.print(a1 == a2);
위와 같은 상황에서는 기존의 알고 있던 지식처럼 False를 반환하게 된다.
동기화
이어서 동기화에 대한 얘기이다. 만약 어떤 데이터가 Immutable 하다는 것은 결국 Multi-Thread 환경에서 동기화 문제가 발생하지 않는 다는 것이다. 이런 의문도 들 수 있다. Constant Pool 에서 같은 데이터를 공유하니 동기화 문제가 생길 수도 있는 것 아닌가요? 결론부터 말하자면 아니다. 해당 이야기는 같은 데이터를 공유했을 때 해당 데이터를 수정했을 경우에 발생하는 이야기이다. String은 위에서 얘기했던 것처럼 데이터를 수정하는 경우 해당 데이터가 변경되는 것이 아닌 새로운 문자열 자체를 생성해서 대입하기 때문에 문제가 없다.
보안성
String이 Mutable 하다면 특정 공격 벡터에 의해서 무결성이 보장되는 데이터가 아니게 될 수 있기 때문이라고 한다.
위와 같은 문제를 봤을 때 우리는 String 연산을 어떻게 진행해야 하는지 의문이 들 것이다. 확실히, 위 처럼 한 개의 문자열을 생성하기 위해서 메모리 공간 9개를 사용하게 되는 것은 쓸데없는 메모리 낭비이다. 그럼 String 연산은 어떻게 하는 것이 좋을까? StringBuffer와 StringBuilder가 있다. 두 개의 차이는 무엇일까?
StringBuffer VS StringBuilder
StringBuffer와 StringBuilder는 String과 다르게 동작한다. 내부적으로 문자열 연산을 할 때 바이트 코드로 전환한 후에 반환하는 형태로 진행이 이루어진다. 따라서, 기존처럼 새로운 메모리 구조를 사용하는것이 아닌 사실상 리스트에 각 문자열에 대한 char 문자들이 이어진 형태로 볼 수 있다. 그리고 둘의 문자열 연산 방식은 동일하다. 그럼 2개의 차이는 무엇일까? 결론부터 얘기하자면 동기화 여부이다.
StringBuffer는 각 메서드별로 Synchronized 키워드가 존재하여 멀티스레드 환경에서도 문자열을 생성하는데 있어서 동시성 문제에 대해서 고려하지 않아도 되지만, StringBuilder는 동기화를 보장하지않는다. 다만 동기화를 위한 모니터 인터페이스의 처리가 이루어지지 않다보니, 당연하게도 StringBuffer보다 속도가 빠르다.
따라서, 만약 멀티스레드 상황에서의 문자열 연산이 필요한 상황이고, 동시성 문제가 발생할 것 같을 때에는 StringBuffer를 사용하는 것이 좋고 그렇지 않다면 StringBuilder를 사용하는 것이 좋을 것이다.
오늘의 회고
자바 커리큘럼을 들으면서 생각보다 그냥 간단히 넘어갈 수도 있는 얘기를 다시 듣게 되어서 복기 할 수 있는 기회가 주어졌던 것 같다! 다시 복습하게 되어서 너무 좋았고, 추가적으로 프리팀원들과 이미지 게임을 진행하였다! 뭔가 팀원들에 나에 대해서 평가한 전체적인 이미지는 자상함과 따뜻함 이라는 느낌이 들었다. 좋게 평가해주셔서 너무 다행이다! 그리고 오늘 jdk 설치 관련해서 mac의 환경변수 설정 때문에 많이 고생했는데 팀원들의 도움과 격려 덕분에 결국 해결 할 수 있게 되었다. 이 건에 관해서는 분명 누군가 비슷한 케이스를 겪을 것 같기에 꼭 트러블 슈팅 관련해서 적을 계획이다!
참고 출처
https://wonit.tistory.com/588
https://12bme.tistory.com/42
https://www.jetbrains.com/help/idea/reference-keymap-mac-default.html#caret_navigation
https://galid1.tistory.com/194
https://broccolies.com/2021/04/22/oracle-jdk%EC%99%80-open-jdk%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%B9%84%EA%B5%90/
'프로그래머스 데브코스' 카테고리의 다른 글
프로그래머스 데브코스 6일차 - 전략패턴 (3) | 2023.06.08 |
---|---|
프로그래머스 데브코스 5일차 (0) | 2023.06.08 |
프로그래머스 데브코스 4일차 - 인터페이스 (0) | 2023.06.07 |
프로그래머스 데브코스 3일차 - OOP 이야기 (0) | 2023.06.06 |
프로그래머스 데브코스 1일차! (0) | 2023.06.02 |