나도 그랬듯이 초보자들은 프로젝트를 시작할 때, 모든 코드를 파일 하나에 다 넣으려고 한다.
하지만 JSP를 예로 들었을때 HTML, CSS, JavaScript, 자바 로직, 데이터베이스 연결 코드까지 한 파일에 뒤섞이면 처음에는 빠르게 결과물이 나오는 것처럼 보인다.
하지만 프로젝트가 조금만 커져도, 그 코드는 누구도 손대고 싶지 않은 '스파게티 코드'가 되어버린다.
잘못된 프로젝트 구조는 유지보수를 악몽으로 만들고, 팀원과의 협업을 불가능하게 한다.
프로젝트에서 왜 구조가 중요한지, 그리고 가장 표준적으로 사용되는 MVC 패턴을 기반으로 어떻게 깔끔한 구조를 만들 수 있는지 설명하겠다.
역할 분담
디자이너는 화면(View)에, 개발자는 로직(Controller, Model)에 집중할 수 있게 한다.
유지보수성
"로그인 로직을 수정해야지" 라고 생각했을 때, LoginServlet.java
와 MemberDAO.java
만 보면 되도록 만들어준다.
수많은 JSP 파일을 뒤질 필요가 없다.
재사용성
잘 만들어진 데이터베이스 처리 로직(MemberDAO
)은 회원 정보가 필요한 어떤 페이지에서든 재사용할 수 있다.
협업 효율성
모든 팀원이 파일이 어디에 위치하고 어떤 역할을 하는지 예측할 수 있어, 코드 충돌을 줄이고 개발 속도를 높인다.
JSP/Servlet 웹 애플리케이션의 구조를 잡는 가장 표준적인 방법은 MVC 패턴이다. 이 패턴은 프로젝트를 세 가지 역할로 명확하게 나눈다.
Model
데이터 그 자체(DTO/VO)와 데이터를 처리하는 비즈니스 로직(Service, DAO). 순수 자바 클래스로 구성된다.
View
사용자에게 보여지는 화면. JSP 파일이 이 역할을 전담한다.
Controller
사용자의 요청을 받고, 모델과 뷰를 연결하는 중재자. 서블릿(Servlet)이 이 역할을 맡는다.
이클립스의 'Dynamic Web Project' 기준으로, 다음과 같은 구조를 따르는 것이 일반적이다.
my-web-project/
├── src/main/java/ # 순수 자바 코드가 위치하는 곳 (Model, Controller)
│ └── com/example/
│ ├── controller/ # 1. Controller (서블릿)
│ │ ├── LoginServlet.java
│ │ └── JoinServlet.java
│ ├── model/ # 2. Model (데이터 관련 클래스)
│ │ ├── dao/ # - DAO (Data Access Object)
│ │ │ └── MemberDAO.java
│ │ └── dto/ # - DTO (Data Transfer Object)
│ │ └── MemberDTO.java
│ └── util/ # 3. 유틸리티 클래스
│ └── DBManager.java (DB 커넥션 풀 관리 등)
│
├── src/main/webapp/ # 웹 콘텐츠가 위치하는 곳 (View)
│ ├── WEB-INF/
│ │ ├── web.xml # - 서블릿 매핑 등 웹 애플리케이션 설정 파일
│ │ └── lib/ # - 외부 라이브러리 (ex: ojdbc.jar)
│ ├── member/ # 기능별로 폴더를 나누면 좋음
│ │ ├── login.jsp
│ │ └── join.jsp
│ ├── main.jsp
│ ├── index.jsp
│ └── css/
│ └── style.css
│
└── pom.xml # (Maven 프로젝트인 경우) 의존성 관리 파일
src/main/java
백엔드 로직의 핵심
controller
패키지
사용자의 모든 HTTP
요청(GET
, POST
)을 가장 먼저 받는 서블릿들이 위치한다. 서블릿은 요청을 분석해서 어떤 모델을 사용할지, 어떤 뷰를 보여줄지 결정하는 '교통경찰' 역할을 한다.
model.dao
패키지
MemberDAO
처럼 데이터베이스에 직접 접근하여 INSERT
, SELECT
, UPDATE
, DELETE
(CRUD) 로직을 전담하는 클래스들이 모여있다.
model.dto
패키지
MemberDTO
처럼 데이터베이스 테이블의 정보를 행(Row) 단위로 담기 위한 객체(JavaBean)들이 위치한다. 계층 간 데이터 전송을 위한 '데이터 그릇'이다.
src/main/webapp
사용자의 눈에 보이는 모든 것
내가 앞으로 공유할 코드들과 나의 Github에 있는 코드들은 교수님께 배운대로 모두 경로를 WebContents 바꿔놨다.
이클립스 기준으로 프로젝트 생성시에 경로를 바꿀 수 있다.
이 폴더 아래의 파일들은 URL을 통해 직접 접근이 가능하다. (단, WEB-INF
폴더 제외)
.jsp
파일 (View)
컨트롤러(서블릿)로부터 전달받은 데이터를 화면에 동적으로 그리는 역할을 한다. 자바 로직은 최소화하고, 화면 표시에만 집중해야 한다.
css
, js
, images
폴더
정적인 리소스 파일들을 위치시킨다.
WEB-INF
보안이 필요한 핵심 설정 공간
이 폴더 안의 파일들은 클라이언트가 URL로 직접 요청할 수 없어 보안에 유리하다.
web.xml
배포 서술자(Deployment Descriptor)라고 부른다. 어떤 URL 요청을 어떤 서블릿으로 연결할지(서블릿 매핑), 필터, 리스너 등 웹 애플리케이션의 핵심 설정을 정의한다.
lib
jdbc.jar
같은 외부 라이브러리(JAR 파일)를 위치시키는 곳이다.
사용자 요청
사용자는 login.jsp
에서 아이디와 비밀번호를 입력하고 '로그인' 버튼을 누른다. 폼의 action
은 /login
을 가리킨다.
Controller (서블릿) 처리
web.xml
에 /login
요청을 처리하도록 매핑된 LoginServlet
이 요청을 가로챈다.
LoginServlet
은 request.getParameter()
로 사용자가 입력한 아이디와 비밀번호를 받는다.
MemberDAO
객체를 생성하고, dao.loginCheck(id, pw)
메서드를 호출하여 DB에 해당 사용자가 있는지 확인한다.
Model (DAO, DTO) 작업:
MemberDAO
는 커넥션 풀에서 DB 커넥션을 얻어와 SELECT
쿼리를 실행한다.
결과가 있다면 로그인 성공, 없으면 실패로 판단하고 그 결과를 LoginServlet
에 반환한다.
View (JSP)로 전달 및 표시:
로그인 성공 시: LoginServlet
은 session.setAttribute("userId", id)
로 세션에 로그인 정보를 기록한 뒤, response.sendRedirect("main.jsp")
를 통해 메인 페이지로 사용자를 보낸다.
로그인 실패 시: 에러 메시지를 request
에 담아 RequestDispatcher
를 이용해 다시 login.jsp
로 포워딩한다. login.jsp
는 전달받은 에러 메시지를 화면에 표시한다.
이처럼 역할에 따라 프로젝트 구조를 명확히 나누는 것은 선택이 아닌 필수이다.
MVC 패턴은 복잡한 웹 애플리케이션을 여러 개발자가 협업하여 만들고, 오랫동안 안정적으로 유지보수할 수 있게 하는 가장 검증되고 안정적인 방법이다.
실무에서 쓰이는 개념들을 하나하나 익힐수록 개인 프로젝트도 관리가 편해지고 뭔가 효율적이고 깔끔한 코딩을 하게되는 것 같다.