# Public static void main(String[] args)는 죽었다

> Clean Markdown view of GeekNews topic #23138. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=23138](https://news.hada.io/topic?id=23138)
- GeekNews Markdown: [https://news.hada.io/topic/23138.md](https://news.hada.io/topic/23138.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2025-09-18T09:11:21+09:00
- Updated: 2025-09-18T09:11:21+09:00
- Original source: [mccue.dev](https://mccue.dev/pages/9-16-25-psvm)
- Points: 17
- Comments: 5

## Summary

Java가 새로운 **void main()** 문법과 **간단한 IO 호출**을 도입하면서, 기존에 복잡하고 장황했던 **public static void main(String[] args)** 및 **Scanner, System.out.println** 구문이 대체됩니다. 이 변화로 초보자가 느끼던 **입문 장벽**이 크게 낮아지고, **코드의 직관성**과 **가독성**이 향상될 것으로 생각됩니다.

## Topic Body

- 이제 Java의 첫 번째 프로그램은 더 이상 **public static void main(String[] args)** 로 시작하지 않고, 단순화된 **void main()** 문법으로 작성 가능해짐  
- 새로운 문법에서는 **IO.readln**과 **IO.println** 같은 간단한 호출만으로 입출력을 처리할 수 있어 코드가 훨씬 직관적으로 바뀜  
- 기존의 **new Scanner(System.in)**, **System.out.println** 같은 장황한 구문은 불필요해짐  
- 그동안의 불편함이 **“마침내 끝남”**, 이제 Java의 기본 구조가 가벼워지면서 **입문 장벽이 낮아지고 학습 친화성**이 크게 향상될 것   
  
---  
  
- 전통적으로 Java는 프로그램 시작을 위해 **`public static void main(String[] args)`** 라는 긴 선언을 요구했음  
- 그러나 2025년 9월 16일 기준, Java의 가장 첫 번째 예제로 여겨지던 `main` 함수의 복잡한 선언문이 새로운 간단한 형태로 대체됨  
- **기존 방식**:  
  ```  
  public class Main {  
      public static void main(String[] args) {  
          Scanner scanner = new Scanner(System.in);  
          System.out.print("What is your name? ");  
          String name = scanner.nextLine();  
          System.out.println("Hello, " + name);  
      }  
  }  
  ```  
- **새로운 방식**:  
  ```  
  void main() {  
      var name = IO.readln("What is your name? ");  
      IO.println("Hello, " + name);  
  }  
  ```  
- 초보자에게는 불필요하게 장황하고, “주술적 주문”처럼 외워야만 했던 구문이라는 비판을 받아왔음  
- **기존 선언문의 번거로움**과 **난해함**을 해소하고, 간결한 문법 도입으로 코드 가독성이 높아졌으며, Java 입문의 진입 장벽이 크게 낮아짐  
  - 더 이상 **Scanner, System.out.println** 등 복잡한 객체 생성과 호출을 기본 예제로 쓰지 않음  
  
> Good Fucking Riddance = “드디어 없어져서 속 시원하다. 잘 가라”

## Comments



### Comment 44146

- Author: kayws426
- Created: 2025-09-22T10:27:42+09:00
- Points: 1

다른 방법이 하나 새로 생겼다고 해서 기존 방법이 죽었다고 말하는 것처럼 들립니다.  
정말로 기존 방법을 사용할 수 없고 새로운 방법을 사용해야만 하는 것이 맞습니까?

### Comment 44055

- Author: jhk0530
- Created: 2025-09-19T00:15:44+09:00
- Points: 1

Wow

### Comment 44038

- Author: jwh926
- Created: 2025-09-18T14:51:14+09:00
- Points: 1

자바를 다시 배워야 하나..

### Comment 44034

- Author: carnoxen
- Created: 2025-09-18T14:11:44+09:00
- Points: 1

The main is dead. Long live the main!

### Comment 43998

- Author: neo
- Created: 2025-09-18T09:11:21+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=45258098) 
* 시간이 지나면서 이런 생소한 코드들이 점점 이해가 되어가는 과정이 그리워질 것 같음. 처음에 Python을 배우고 나서 Java로 넘어갔을 때 'void'나 'String[]' 같은 타입이 무엇을 의미하는지 몰라서 신기했음. 타입을 배우고 나니 이해가 되었고, 그 다음엔 class와 object 개념, 그리고 main이 왜 static 메서드로 존재하는지 알게 되었음. 더 깊이 들어가면서 이 클래스가 언제 호출되는지도 배우면서 처음엔 단순한 보일러플레이트 같던 코드가 점점 말이 되기 시작했음. 아마 경력이 많은 Java 개발자는 저보다 더 많은 의미를 한 줄에 읽어낼 수 있을 것 같음. 그래도 이제는 없어져서 속 시원함

  * 정말 속 시원함. 지난 30년간 소프트웨어 교육은 개발자에게 '공학'이라는 이름으로 쓸데없는 복잡성을 생산하도록 가르쳐왔음. 예를 들어, A 개발자는 엔트리 포인트나 콜백, 인터페이스 등 무엇이든 필요에 따라 클래스를 만듦. 그 결과 우리는 클래스를 가지게 됨. B 개발자는 클래스에 인스턴스 변수를 추가하며 전체를 변경 가능하게 만들고, 이로 인해 "구현의 진흙탕"이 형성됨. 이후 다른 개발자가 코드 재사용을 위해 상속을 추가하지만, 이로 인해 더 많은 복잡성과 동적 디스패치 악몽이 생김. 결국 참조 순환(rference cycle)이 생겨 객체들의 연결관계가 꼬이게 됨. 시간이 지날수록 리팩토링은 점점 힘들고 귀찮아져서, 결국 코드를 지우고 새로 시작하게 됨

  * 대학에서 처음 프로그래밍을 배울 때 이 코드를 보고 교수님이 "보통은 코드 설명을 다 해주겠지만, 이 부분은 그냥 일단 받아들이라"고 했던 기억이 있음. 나중에 돌아보니 내가 저 코드를 다 이해하고 있다는 걸 깨달았을 때 꽤 멋졌던 경험임. 그래도 이제 시대가 바뀌는 것 같아서 속이 후련함

  * 사실 static 클래스 메서드는 프로그램의 단일 진입점(entry point)이 반드시 클래스와 관련되어야 한다는 현실 부정에 가까움. C++ 뿐만 아니라 Python, Ruby처럼 절차지향의 현실을 인정한 언어도 있는데, Java는 마치 사용자의 눈을 가리고 "완벽한 OOP 세계"로 몰아넣는 듯함

  * 구식 class 기반 방식을 알던 입장에서 새로운 스타일(Java 21의 unnamed classes 등)이 더 질문을 던지는 것 같음. 정말 "모든 것이 객체"인 Java를 절차지향 언어로 바꾼 건지 의문임. 만약 커맨드라인 인자가 필요할 때는 어떻게 해야 하는지 궁금함. 나는 주로 Java를 피해서 잘 몰랐지만, 이건 진심 궁금해서 묻는 질문임

* Java 1.2 시절에는 표준입력을 다음과 같이 읽었음. Scanner class는 처음이라 생소함  
```java
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String input = in.readLine();
```

  * 나도 비슷한 경험임. 오래전에 Java를 하다 보니 public static void main 패턴은 익숙했는데, Scanner 사용법이 어색해서 이유를 정확히 이해하지 못했었음

* 내 경험상 대형 프로젝트에서 수십 년 동안 일해왔기 때문에, main 함수가 어떻게 써있는지는 내 일상이나 커리어에는 전혀 영향을 끼치지 않음. 여러분은 이런 변화가 실제로 얼마나 영향을 주는지 궁금함

  * Android 개발자라면 main이란 개념 자체가 없었음. 솔직히 trivial한 Hello World의 구현이 못생겼다고 해서 그 언어가 그 이상을 잘 처리하는지의 척도가 된다고 생각하지 않음

  * 많은 사람들이 Java로 프로그래밍 입문을 하면서 main을 수십 번 타이핑했지만, 그게 무슨 의미인지 전혀 모르고 있었음

  * 실무 개발자에게는 크게 영향이 없지만, 초보자에게는 중요한 문제임. 예전에 Java를 가르쳤는데, "Hello, World"를 찍기 전에 외워야 하는 마법 주문(보일러플레이트)이 학생들에겐 진입장벽으로 작동했음

  * 명령줄 인터페이스를 만든다면 영향이 있을 수 있겠지만, Java로 CLI를 만드는 경우 그리 많지 않음

  * 나는 최근 5년 정도 codebase에서 main을 본 적이 없음. 그리고 거의 새로운 클래스를 직접 만들기보다는 보통 프레임워크 특정 클래스를 구현하거나 확장하는 형태임

* JEP 445: Unnamed Classes and Instance Main Methods가 Java 21에 릴리스됨  
[https://openjdk.org/jeps/445](https://openjdk.org/jeps/445)

  * 아쉽게도 이 기능이 단일 파일 프로그램에서만 쓸 수 있도록 나온 점이 아쉬움. 패키지 레벨 메소드나 여러 클래스/레코드를 파일에 둘 수 있게 허용했다면 Java가 더 좋아졌을 것 같음. 하지만 어쨌든 "Java 코드를 일반적으로 작성하는 방식에 미치는 영향"은 원하지 않았던 것 같음

* 30년만에 Java가 마침내 꽤 괜찮은 언어로 진화하는 모습이 놀라움

  * 나는 프레임워크 없이 순수하게 Java와 C++로 많은 코드를 짰었고, 이 때는 보일러플레이트가 훨씬 적고 간단했음. 진짜 편리함을 체감한 건 약 10년 전 lambda가 추가됐을 때임. 람다 도입 이후 코드의 분량이 줄어들고, 굉장히 쾌적해짐을 느꼈음. Java는 오래도록 엄청나게 명시적이고, 사소한 것에도 오타 체크만 해주는 것 같은 불필요한 반복문이 많았음. Kotlin이 뜨기 전까지 이런 분위기였는데, 이후 lambda와 익명클래스 등으로 개발 경험이 크게 나아졌음

  * Java가 끔찍했던 언어라는 주장은 너무 과장임

  * 반면 Python 같은 언어는 30년이 지났어도 아직도 불편하다고 생각함

  * 아마도 정말 나쁜 언어를 경험해 본 적이 없거나 "끔찍하지 않음"의 기준이 굉장히 높다고 추측함

  * 그래도 Java는 뛰어난 하위호환성과 패키지 관리 시스템이 있다는 점은 대단함

* 이전 댓글에서 언급한 내용인데, Java 개발자로서 python은 오히려 이상하지만 지금 방식이 나쁘다고 생각하지 않음. 다만 추상화의 기본을 이해하지 못한 채 바로 프로그래밍을 하면 "프로그래밍 입문"에는 좋은 선택이 아닐 수 있다고 봄. 어떤 도구가 목적 지향, 패러다임 지향 등으로 설계되면 때로는 극단적 추상화가 일어날 수 있음. Java는 OOP에 초점을 두고 설계되어있기 때문에 인터페이스 같은 블록 단위 사고가 자연스러움. 메서드/클래스의 접근제한자 역시 누구를 위한 것인지에 따라 명확히 구분함(public, protected, default, private 등). 즉, Java로의 입문은 사용자(프로그래머)를 향한 interface의 노출로부터 시작함. 이상해 보일 순 있지만, 적어도 일관성은 있음

  * 무거운 문법은 OOP와 직접적인 상관이 없음. Java가 등장하기 전 OOP의 어떤 정의도 "함수는 클래스 밖에 존재할 수 없다"라고 말하지 않았음. Sun은 오랫동안 스탠드얼론 함수 개념을 거부했고(정적 import는 Java 5, 클로저는 Java 8, compact source 파일/instance main은 이제서야 도입), 그 결과 모든 함수가 여전히 클래스 내부에 있음(실용성과 호환성 때문이지, 철학적 이유는 아님). "모든 것이 객체"를 고집하려 했지만, 실제로는 primitive 값을 도입하는 모순도 있었음. 함수가 반드시 클래스 내부에 있어야 할 실용적 이득은 없음. 오히려 정수와 같은 값에 메서드를 정의할 수 있었으면 더 좋았을 것임. 참고로 Smalltalk 같은 "순수 OOP" 언어에서는 클래스 밖에 함수를 자유롭게 정의할 수 있고 REPL에서도 직접 코드를 실행할 수 있음  
    [관련 링크](https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html)

  * Hello World가 중요한 이유는, 프로그램 실행을 위한 보일러플레이트와 실제 출력 코드를 동시에 보여줄 수 있기 때문임. 또한 빌드 도구 설정이라는 측면에서도, 일부는 '보일러플레이트'라기보다는 실습/도구 설정을 위한 절차임. 이를 바로 설명해주면 큰 문제는 아님

* 만약 컴파일러 트릭으로 내부에서 클래스를 생성한다면 그저 보여주기식 변화에 불과함. 다른 top-level 함수가 허용되지 않는다면 결국 특이 케이스일 뿐이라 여전히 어색함

  * C#은 9.0부터 top level statement를 지원하지만, 결국 내부적으로는 정적 메서드로 변환됨. 여러 top level 함수도 비슷하게 동작함. 실제 예시: [C# decompiled example](https://lab.razor.fyi/#41rAyMUVUJSfXpSYq5dcLDSRsbQ4My9dIbiyuCQ115qLyzk_rzg_J1UvvCizJNUnMy9VQykkv0AhJ7UsNUehuCSxJDU3Na9ESdOaiyskv8AHJOybWpKRn6IBEirLz0xRQBfnquZSUFBQwGtyLlgtyNhaL6bk4ihOjn-zzk9alSbAksAIAA)  
    이런 트릭은 실제로 유용한 컴파일러 변환의 예시임(closure, async state machine 등). 이번 변화도 그런 셈임

  * C/C++에서 main()에서만 기본 return 0이 적용되는 것도 비슷하게 어색하게 느껴짐

  * 만약 main만 top-level 함수로 허용되고, 다른 것은 불가능하다면 이 역시 별로 좋은 변화가 아니라고 봄

* Java 코드 예제에서 import 라인을 왜 생략하는지 이해가 안됨. 보일러플레이트의 복잡함을 보여주려면 import도 반드시 써줘야 할 것 같음

  * 보통 IDE가 import를 자동 관리해주기 때문에 신경쓰지 않음. IO 관련 클래스라면 compact 예제에선 따로 import가 필요 없어서 실제로 코드가 저대로 동작함

  * 대부분의 Java IDE는 import를 자동으로 관리해줌

* public static void main(String[] args)라는 엔트리포인트를 C#처럼 top-level statement 패러다임으로 추상화하면 보일러플레이트를 줄이고 코드가 간결해질 것 같은데, 왜 그렇게 안 했는지 궁금함

  * [Paving the on-ramp](https://openjdk.org/projects/amber/design-notes/on-ramp) 문서를 참고하길 바람

  * 엔트리 포인트가 특수한 예외 케이스로 존재한다면 이미 OOP의 한계를 인정하는 셈임. 그렇다면 과감하게 새로운 대안을 설계하는 편이 낫지 않을까 생각함

* unnamed class 방식을 활용하면 전역 변수 구현이 쉬워지고, 핫리로드도 가능하면서 문법이 간결해진다는 점이 좋다고 생각함. 한편, Java 파일은 이름이 public class와 동일해야 한다는 제약이 있어서, 차라리 `public class X { }` 부분을 옵션으로 두고 필요한 경우만 쓰게 했어도 됐을 듯함. 왜 익명 클래스를 도입해야 했는지 잘 이해가 안 됨  
컴파일러는 여전히 HelloWorld.class 같은 클래스로 변환하므로, 그 이름은 구현 세부사항일 뿐 실제로 소스코드에선 직접적으로 쓸 순 없음  
[https://openjdk.org/jeps/445](https://openjdk.org/jeps/445)
