자바에서 클래스를 만들다 보면 파일이 점점 많아집니다. 처음에는 Post 클래스 하나만 있어도 충분하지만, 기능이 늘어나면 댓글, 태그, 검색, 알림처럼 관련 클래스가 계속 생깁니다.

이때 모든 클래스를 한 폴더에 넣어두면 코드를 찾기 어렵습니다. 또 서로 다른 기능에서 같은 이름의 클래스를 만들 때도 문제가 생길 수 있습니다.

패키지는 이런 문제를 줄이기 위해 클래스를 역할별 공간으로 나누는 문법입니다. import는 다른 공간에 있는 클래스를 현재 파일에서 짧게 부르기 위한 문법입니다.

앞에서 클래스와 객체가 아직 헷갈린다면 자바 클래스와 데이터 정리 - 객체로 묶어 생각하기를 먼저 보면 더 이해하기 쉽습니다.


패키지를 왜 나눌까?

블로그 프로그램을 만든다고 생각해보겠습니다. 처음에는 글 하나만 다루면 되기 때문에 클래스가 많지 않습니다.

Post
PostPrinter

하지만 기능이 늘어나면 클래스 이름만 보고도 역할을 구분하기 어려워집니다.

Post
PostPrinter
Comment
CommentPrinter
Tag
TagPrinter
SearchKeyword
SearchResult
Notification
NotificationSender

이런 코드를 기능별로 나누면 훨씬 읽기 쉽습니다.

post
  Post
  PostPrinter

comment
  Comment
  CommentPrinter

notification
  Notification
  NotificationSender

패키지는 단순히 폴더를 예쁘게 나누는 기능이 아닙니다. 코드가 커졌을 때 어디에 무엇이 있는지 찾기 쉽게 만드는 기준입니다.


package는 현재 클래스의 위치를 적는 문법

어떤 클래스가 어느 패키지에 속하는지는 파일 맨 위에서 선언합니다.

package com.deepc.blog.post;

public class Post {
    String title;
    String content;
}

위 코드는 Post 클래스가 com.deepc.blog.post 패키지에 있다는 뜻입니다. package 선언은 보통 파일의 가장 위에 작성합니다.

댓글 클래스는 다른 패키지에 둘 수 있습니다.

package com.deepc.blog.comment;

public class Comment {
    String writer;
    String message;
}

이제 PostComment는 서로 다른 패키지에 있는 클래스입니다. 기능별로 위치가 나뉘었기 때문에 프로젝트가 커져도 코드를 찾기 쉬워집니다.


패키지 이름은 폴더 구조와 맞춘다

패키지 이름은 실제 폴더 구조와 맞추는 것이 기본입니다.

src
└── com
    └── deepc
        └── blog
            ├── post
            │   └── Post.java
            ├── comment
            │   └── Comment.java
            └── notification
                └── NotificationSender.java

Post.java는 아래 패키지에 있어야 자연스럽습니다.

package com.deepc.blog.post;

NotificationSender.java는 아래 패키지에 있어야 자연스럽습니다.

package com.deepc.blog.notification;

패키지 이름과 폴더 위치가 다르면 IDE가 경고를 보여주거나, 클래스를 찾는 과정에서 헷갈릴 수 있습니다. 처음에는 패키지 이름을 파일이 놓인 위치를 코드로 적은 것이라고 생각하면 됩니다.


같은 패키지와 다른 패키지의 차이

같은 패키지에 있는 클래스는 보통 클래스 이름만으로 사용할 수 있습니다.

package com.deepc.blog.post;

public class PostPrinter {
    public void print(Post post) {
        System.out.println(post.title);
    }
}

PostPrinterPost가 같은 com.deepc.blog.post 패키지에 있다면 Post라고 바로 적을 수 있습니다.

하지만 다른 패키지의 클래스를 사용할 때는 자바에게 그 클래스가 어디 있는지 알려줘야 합니다. 이때 import를 사용합니다.


import는 다른 패키지 클래스를 짧게 부르게 해준다

글 상세 화면에서 글과 댓글을 함께 출력한다고 생각해보겠습니다. PostViewview 패키지에 있고, PostComment는 각각 다른 패키지에 있습니다.

package com.deepc.blog.view;

import com.deepc.blog.post.Post;
import com.deepc.blog.comment.Comment;

public class PostView {
    public void render(Post post, Comment comment) {
        System.out.println(post.title);
        System.out.println(comment.message);
    }
}

import를 작성했기 때문에 코드 안에서는 Post, Comment처럼 짧게 쓸 수 있습니다. 만약 import가 없다면 아래처럼 전체 패키지 이름을 함께 적어야 합니다.

com.deepc.blog.post.Post post;
com.deepc.blog.comment.Comment comment;

전체 이름을 적는 방식도 틀린 것은 아닙니다. 다만 계속 반복하면 코드가 길어지고 읽기 어려워집니다.


import는 복사가 아니다

처음에는 import가 다른 파일의 코드를 복사해오는 것처럼 느껴질 수 있습니다. 하지만 실제 의미는 다릅니다.

import이 파일에서 어떤 클래스를 짧은 이름으로 부를지 알려주는 선언입니다. 클래스 파일이 현재 파일 안으로 들어오는 것은 아닙니다.

import com.deepc.blog.post.Post;

이 한 줄은 아래처럼 이해하면 됩니다.

앞으로 이 파일에서 Post라고 쓰면
com.deepc.blog.post.Post를 뜻하는 것으로 볼게요.

그래서 import는 코드 위쪽에 모아두면 좋습니다. 현재 파일이 어떤 클래스에 의존하는지 한눈에 볼 수 있기 때문입니다.


이름이 같은 클래스가 있으면 전체 이름을 쓴다

패키지가 다르면 같은 이름의 클래스를 만들 수 있습니다. 예를 들어 블로그에도 Member가 있고, 관리자 기능에도 Member가 있을 수 있습니다.

com.deepc.blog.member.Member
com.deepc.admin.member.Member

둘 다 클래스 이름은 Member입니다. 하지만 패키지까지 포함하면 서로 다른 클래스입니다.

한 파일에서 두 클래스를 모두 써야 한다면 한쪽은 import하고, 다른 한쪽은 전체 이름으로 적는 방식이 안전합니다.

package com.deepc.admin.report;

import com.deepc.blog.member.Member;

public class MemberReport {
    public void print() {
        Member blogMember = new Member();
        com.deepc.admin.member.Member adminMember =
                new com.deepc.admin.member.Member();
    }
}

이렇게 하면 어떤 Member를 말하는지 분명합니다. 짧게 쓰는 것보다 헷갈리지 않게 쓰는 것이 더 중요할 때가 있습니다.


별표 import는 조심해서 사용한다

아래처럼 *를 사용하면 특정 패키지 안의 여러 클래스를 한 번에 짧게 사용할 수 있습니다.

import com.deepc.blog.post.*;

간단해 보이지만, 처음 공부할 때는 어떤 클래스가 어디에서 왔는지 흐려질 수 있습니다. 그래서 입문 단계에서는 필요한 클래스를 직접 적는 편이 더 좋습니다.

import com.deepc.blog.post.Post;
import com.deepc.blog.post.PostPrinter;

이렇게 작성하면 파일 위쪽만 봐도 이 코드가 어떤 클래스와 연결되어 있는지 알 수 있습니다. 코드가 조금 길어져도 읽는 사람에게 더 친절합니다.


java.lang은 자동으로 import된다

자바에서 String, System, Math 같은 클래스는 자주 사용합니다. 그런데 우리는 보통 아래 코드를 직접 적지 않습니다.

import java.lang.String;
import java.lang.System;

java.lang 패키지는 자바가 기본으로 자동 import해줍니다. 그래서 String, System.out.println()을 바로 사용할 수 있습니다.

반면 Scanner, ArrayList 같은 클래스는 직접 import해야 합니다.

import java.util.Scanner;
import java.util.ArrayList;

처음에는 java.lang은 기본으로 열려 있고, 나머지는 필요할 때 직접 가져온다고 생각하면 됩니다.


패키지 이름을 정할 때 기억할 것

패키지 이름은 보통 소문자로 작성합니다. 클래스 이름은 PostView처럼 대문자로 시작하지만, 패키지 이름은 post, comment, view처럼 소문자를 사용합니다.

많은 프로젝트에서는 도메인을 거꾸로 적는 방식도 사용합니다.

com.deepc.blog
com.example.todo
org.sample.study

도메인을 거꾸로 쓰는 이유는 이름 충돌을 줄이기 위해서입니다. 다만 개인 학습 단계에서는 너무 복잡하게 시작할 필요는 없습니다.

처음에는 아래 기준으로 충분합니다.

기준예시
소문자로 작성post, comment, view
역할 중심으로 나누기글, 댓글, 화면, 알림
너무 깊게 나누지 않기작을 때는 단순하게 시작
폴더 구조와 맞추기com.deepc.blog.post

작은 블로그 예시

아래 구조를 기준으로 생각해보겠습니다.

com.deepc.blog.post
  Post

com.deepc.blog.comment
  Comment

com.deepc.blog.view
  PostView

PostView는 글과 댓글을 화면에 보여주는 역할입니다. 그래서 PostComment를 사용해야 합니다.

package com.deepc.blog.view;

import com.deepc.blog.post.Post;
import com.deepc.blog.comment.Comment;

public class PostView {
    public void render(Post post, Comment comment) {
        System.out.println("글 제목: " + post.title);
        System.out.println("댓글: " + comment.message);
    }
}

여기서 핵심은 아래와 같습니다.

  1. PostView의 현재 위치는 com.deepc.blog.view입니다.
  2. PostComment는 다른 패키지에 있습니다.
  3. 그래서 import로 짧게 사용할 수 있게 했습니다.

패키지는 코드를 찾기 쉽게 나누는 기준입니다. import는 그 나뉜 위치의 클래스를 현재 파일에서 편하게 쓰기 위한 문법입니다.


실수하기 쉬운 부분

패키지를 처음 사용할 때는 아래 부분을 자주 헷갈립니다.

실수확인할 점
package를 파일 중간에 작성파일 맨 위에 작성했는지 확인합니다
폴더 위치와 패키지 이름이 다름경로와 선언이 같은 흐름인지 봅니다
다른 패키지 클래스를 바로 사용import가 필요한지 확인합니다
같은 이름의 클래스를 섞어 사용전체 패키지 이름으로 구분합니다
패키지를 너무 잘게 나눔작은 프로젝트는 단순하게 시작합니다

패키지는 처음부터 완벽하게 나누는 기술이 아닙니다. 코드가 많아질 때 찾기 쉽고 헷갈리지 않도록 정리하는 습관에 가깝습니다.


정리

패키지는 자바 클래스를 역할별로 나누는 방법입니다. 클래스가 많아졌을 때 코드를 찾기 쉽게 만들고, 같은 이름의 클래스가 충돌하지 않게 도와줍니다.

package는 현재 클래스가 어느 패키지에 속하는지 적는 문법입니다. import는 다른 패키지에 있는 클래스를 짧은 이름으로 사용할 수 있게 해주는 문법입니다.

처음에는 아래처럼 기억하면 됩니다.

package는 현재 파일의 위치를 적는 문법입니다.
import는 다른 위치의 클래스를 짧게 부르는 문법입니다.

패키지를 잘 나누면 프로젝트가 커져도 코드를 찾기 쉬워집니다. 지금 단계에서는 post, comment, view처럼 역할별로 나누는 감각부터 익히면 충분합니다.