상세 컨텐츠

본문 제목

Java `public static void main(String[] args)` 진짜 의미 + Scanner 입력받기

개발

by 코드스키 2026. 7. 24. 07:43

본문

 Java main 시그니처와 Scanner 입력 대표 이미지
Java main 시그니처와 Scanner 입력 대표 이미지

Java 실습 첫날, IntelliJ에 코드를 짜다가 두 번째 줄에 빨간 줄이 그어졌다. 디버깅도 안 됐다. 살펴보니 static void main(String args[])라고 적어놓고 public을 빠뜨린 게 원인이었다. 그동안 그냥 외워서 쓰던 public static void main(String[] args)인데, 다섯 단어나 되는 이 한 줄에서 각 단어가 왜 거기 있어야 하는지 한 번도 제대로 생각해본 적이 없다는 걸 그때 깨달았다. C를 다루다가 Java로 넘어오니 더 헷갈렸다.

한 단어씩 뜯어보면

public static void main(String[] args) {
    // ...
}

public부터 보자. JVM이 이 메서드를 클래스 외부에서 호출해야 하는데, JVM은 내가 짠 클래스 내부의 일부가 아니라 완전히 바깥에 있는 시스템이다. 그래서 외부에서 접근 가능하도록 public이어야 한다. private static void main(...)으로 짜면 컴파일은 되는데 실행할 때 JVM이 main을 못 찾아서 "Main method not found" 에러가 뜬다.

그다음 static이 사실 제일 중요한 키워드다. 일반 인스턴스 메서드는 객체를 먼저 만들어야 호출 가능한데, 프로그램이 막 시작하는 순간에는 아직 어떤 객체도 존재하지 않는다. JVM이 Main 클래스를 메모리에 로드했을 뿐 new Main()을 한 적은 없다. static 메서드는 객체 없이 클래스 이름으로 바로 호출할 수 있어서, JVM이 Main.main(args)처럼 클래스를 통해 직접 부를 수 있게 된다. static이 빠지면 non-static method main cannot be referenced from a static context 에러가 난다.

void는 반환값이 없다는 뜻인데, JVM이 정한 시그니처가 그렇다. C랑 비교하면 재밌는 차이다.

// C
int main(int argc, char *argv[]) {
    return 0;  // 종료 코드
}
// Java
public static void main(String[] args) {
    // 반환 안 함
}

C는 종료 상태 코드를 return 0;으로 넘기는데 Java는 void라 그게 안 된다. 그럼 종료 코드는 어떻게 주냐면 System.exit(1); 같은 걸 쓴다.

main은 키워드가 아니라 그냥 이름이다. JVM이 시작점으로 찾는 이름이 정확히 main이라 이렇게 쓰는 거지 언어 차원에서 예약된 단어는 아니다. mainn이나 Main으로 바꾸면 컴파일은 되지만 JVM이 실행할 때 못 찾는다.

마지막으로 String[] args는 JVM이 사용자가 명령줄에 적은 인자들을 문자열 배열로 넘겨주는 자리다. args라는 이름은 관습일 뿐 강제는 아니다.

java Main hello world 123

이렇게 실행하면 args[0]"hello", args[1]"world", args[2]"123", args.length는 3이 된다. C와 비교하면 argc가 따로 없다는 게 차이인데, Java 배열은 자기 길이를 알고 있어서 args.length로 바로 접근하면 된다.

Scanner로 입력받기

main 안에서 키보드 입력을 받을 때는 보통 Scanner를 쓴다.

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("이름을 입력하세요: ");
        String name = scanner.next();

        System.out.print("나이를 입력하세요: ");
        int age = scanner.nextInt();

        System.out.println(name + "님은 " + age + "살입니다.");

        scanner.close();
    }
}

Scannerjava.util 패키지에 있어서 맨 위에 import를 박아야 한다. IntelliJ에서는 Scanner라고 치고 Alt+Enter를 누르면 자동으로 추가해준다. next()는 공백 전까지 한 단어, nextLine()은 줄 끝까지, nextInt()/nextDouble()/nextBoolean()은 각각 int·double·boolean을 읽는다.

여기서 처음에 진짜 헷갈렸던 게 next()/nextInt() 다음에 nextLine()을 부르는 조합이다. nextInt()는 공백이나 개행 직전까지만 읽고 멈추기 때문에, 입력 버퍼에 개행 문자가 그대로 남는다. 그 상태에서 nextLine()을 부르면 남아있던 개행을 즉시 읽어서 빈 문자열을 반환하고 끝나버린다.

int age = scanner.nextInt();        // 25 입력 → age=25, 버퍼에 \n 남음
String name = scanner.nextLine();   // 즉시 빈 문자열 반환! 입력 못 받음

해결은 nextInt() 다음에 더미로 nextLine()을 한 번 더 불러서 남은 개행을 처리해주는 거다.

int age = scanner.nextInt();
scanner.nextLine();  // 남은 개행 처리
String name = scanner.nextLine();

그밖에 헷갈렸던 것들

String[] argsString args[]는 둘 다 문법적으로 맞다. 배열 표기 []가 타입 뒤에 붙느냐 변수명 뒤에 붙느냐의 차이일 뿐이고, C/C++에서는 변수명 뒤가 자연스럽지만 Java 컨벤션은 타입 뒤(String[] args)를 권장한다. 가변 인자 문법인 String... args도 되는데 main에서는 거의 안 쓴다.

publicstatic의 순서를 바꿔서 static public void main(...)으로 써도 컴파일은 된다. 다만 다들 public static void로 쓰니까 그냥 관습을 따르는 게 가독성에 좋다.

IntelliJ에서 빨간 줄이 뜨는 흔한 원인을 정리하면, public 빠뜨림, static 빠뜨림, String[] args 빠뜨림, 클래스 이름과 파일명 불일치(public class Main인데 파일명이 Main.java가 아닌 경우), 세미콜론 빠뜨림 정도다.

main도 메서드라서 오버로딩이 가능한데, JVM이 시작점으로 인식하는 건 정확히 public static void main(String[]) 하나뿐이다.

public class Main {
    public static void main(String[] args) {
        main(42);          // 우리가 직접 호출
        main("hello");     // 우리가 직접 호출
    }
    public static void main(int n) { System.out.println("int: " + n); }
    public static void main(String s) { System.out.println("String: " + s); }
}

JVM은 String[]를 받는 main을 호출하고, 그 안에서 우리가 다른 오버로드를 직접 부를 수 있다. 실제로는 거의 안 쓰는 패턴이다.

참고한 자료는 DigitalOcean의 "Understanding public static void main(String[] args) in Java", GeeksforGeeks의 Java main() Method 설명, Oracle Java Language Specification의 메서드 선언 부분, 그리고 Coderanch 포럼에서 main 시그니처가 왜 그렇게 생겼는지 다룬 글이다.

언어 문법과 실행기 규칙을 나눠 보기

public static void main(String[] args)는 단순 주문이 아니라 Java 실행기가 시작 메서드를 찾는 규칙과 연결된다. static이라 객체 생성 없이 호출할 수 있고, String[] args에는 명령행 인자가 들어온다. Scanner는 표준 입력 스트림을 토큰 단위로 읽는 별도 도구다.

Oracle java launcher 공식 문서
Oracle java launcher 공식 문서
Java Scanner API 문서
Java Scanner API 문서
Java Virtual Machine Specification
Java Virtual Machine Specification
dev.java 공식 학습 페이지
dev.java 공식 학습 페이지

nextInt() 뒤에 nextLine()을 바로 호출하면 남은 줄바꿈을 읽는 문제도 토큰 입력과 줄 입력의 경계를 알면 설명된다. 입력 방식을 섞을 때는 한 줄을 읽고 직접 변환하거나, 남은 개행을 소비하는 코드를 의도적으로 둔다.

공식 자료

관련글 더보기