본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로웹 개발 › 프론트엔드 › JSP

MVC2패턴으로 구현한 jsp & servlet 게시판

목차

MVC2패턴으로 구현한 jsp & servlet 게시판

Mysql, java 1.7, apache tomcat 7 필요

CRUD 계시판구현 및 파일 업로드 처리

DB : 커넥션 풀 이용

파일 업로드 : cos 라이브러리

커넥션 풀 context 경로 : META-INF/context.xml

DB 테이블

create table board (
  code int(5) not null primary key auto_increment,
  name varchar(100),
  price int(8), 
  filename varchar(50),
  filesize integer,
  filedate date,
  content varchar(1000)
);

파일 다운로드

[startBootServlet.zip

다운로드](</backup-assets/tistory/35/001.zip>)

이 예제는 Java 1.7과 Tomcat 7을 사용하던 시기의 학습용 자료다. 압축 파일의 코드와 의존 라이브러리를 실행하기 전에 별도 개발 환경에서 확인해야 한다.

요청 처리 흐름

flowchart LR
  브라우저 --> 서블릿[Servlet 컨트롤러]
  서블릿 --> 모델[게시판 서비스·DAO]
  모델 --> DB[(MySQL)]
  서블릿 --> JSP[JSP 뷰]
  JSP --> 브라우저

MVC2에서는 서블릿이 요청을 받아 입력을 해석하고, 모델이 조회·저장 작업을 수행한 뒤, JSP가 결과를 출력한다. 목록 조회는 GET, 글 생성은 POST처럼 요청의 역할을 분리하면 흐름을 추적하기 쉽다. 저장 후 새로고침으로 중복 등록되지 않도록 성공 시 리다이렉트하는 방식도 고려한다.

원래 SQL은 name, price 컬럼이 있어 일반적인 게시글 제목·본문 구조와 다르다. 실제 게시판으로 바꾸려면 컬럼 의미와 제약 조건을 먼저 정해야 한다. DB 쿼리에는 준비된 문장의 바인딩 변수를 사용하고, 업로드 파일은 이름·크기·형식을 검증한 뒤 웹 공개 경로와 분리해 저장한다. 다운로드 응답에서는 저장된 원본 경로를 사용자 입력으로 직접 조합하지 않는다.

목록·작성·상세의 책임을 나누기

기존 압축 파일은 Java 1.7, Tomcat 7, 당시 라이브러리 조합을 담은 역사적 학습 자료다. 현재 서비스에 바로 배포하는 절차로 취급하지 않는다. 현대 Jakarta Servlet 프로젝트로 같은 게시판을 새로 만든다면 요청별 책임부터 정한다.

요청Servlet의 일서비스·DAO의 일JSP의 일
GET /posts페이지 번호 검증정렬 기준과 범위로 목록 조회이스케이프된 제목 표시
GET /posts/{id}숫자 ID 검증글 조회와 공개 권한 확인본문·첨부 표시
POST /posts로그인·CSRF·입력 길이 검사글 저장성공 뒤 리다이렉트된 화면 표시
POST /posts/{id}/delete작성자 인증·CSRF 검사소유권과 삭제 처리결과 메시지 표시

이 표의 URL은 설계 예시다. 서블릿 매핑을 실제로 구현할 때는 pathInfo 파싱과 존재하지 않는 ID 처리까지 정한다. 조회 화면에서 삭제 기능을 GET 링크로 만들면 크롤러나 미리 보기 요청만으로 상태가 바뀔 수 있으므로 상태 변경에는 POST 같은 요청을 사용한다.

sequenceDiagram
  participant B as 브라우저
  participant C as Servlet
  participant S as PostService
  participant DB as DB
  participant V as JSP
  B->>C: POST /posts
  C->>C: 인증·입력·CSRF 검사
  C->>S: createPost(command)
  S->>DB: INSERT (바인딩 변수)
  DB-->>S: 생성된 ID
  S-->>C: ID
  C-->>B: 303 See Other: /posts/{id}
  B->>C: GET /posts/{id}
  C->>V: request 속성 전달
  V-->>B: HTML

성공 후 새 URL로 이동하는 Post/Redirect/Get 흐름은 브라우저 새로고침이 같은 POST를 다시 보내는 문제를 줄인다. 그래도 네트워크 재시도나 중복 클릭은 가능하므로 중복 생성이 업무상 문제라면 요청 식별자나 서버 측 중복 방지 규칙을 추가한다. JSP는 SQL을 실행하지 않고 서블릿이 전달한 화면 데이터를 렌더링한다.

첨부 파일 업로드를 붙일 때

Servlet의 @MultipartConfig로 요청과 파일 크기의 상한을 먼저 둔다. 예를 들어 파일 하나는 5MiB, 전체 요청은 6MiB로 제한할 수 있다.

@WebServlet("/posts")
@MultipartConfig(
    maxFileSize = 5L * 1024 * 1024,
    maxRequestSize = 6L * 1024 * 1024
)
public class PostServlet extends HttpServlet {
    // doGet / doPost에서 요청을 구분하고 서비스에 위임
}

Part.getSubmittedFileName()은 클라이언트가 보낸 이름이라 저장 경로로 직접 쓰지 않는다. 저장 이름은 서버가 UUID처럼 새로 만들고, 업로드 디렉터리는 웹 공개 경로 밖에 둔다. 크기뿐 아니라 허용 파일 형식, 실제 내용, 사용자별 업로드 권한을 검사한다. 다운로드 요청에서는 DB에 저장한 파일 ID로 경로를 조회하고 그 글을 읽을 권한이 있는지 다시 확인한다.

DB INSERT와 파일 저장은 하나의 DB 트랜잭션으로 자동 묶이지 않는다. 파일을 쓴 뒤 INSERT가 실패하면 고아 파일을 지우거나 나중에 정리하는 절차가 필요하다. 반대로 DB를 먼저 저장하고 파일 쓰기가 실패하면 게시글 상태를 실패로 바꿀 수 있어야 한다. 이 순서를 명시하지 않으면 재시도할 때 같은 첨부가 여러 개 남는다.

원래 테이블의 price와 name은 상품 예제에 가까운 컬럼이다. 게시판이라면 title, body, author_id, created_at의 의미와 NOT NULL 제약, 길이 상한을 먼저 정한다. 요청 문자열을 이어 붙여 SQL을 만들지 않고 PreparedStatement의 바인딩 변수를 쓴다. JSP에서는 사용자 제목·본문을 HTML로 바로 연결하지 않고 이스케이프한다.

검증은 “게시글이 한 번 보인다”에서 끝나지 않는다. 빈 제목, 긴 본문, 권한 없는 삭제, 크기 초과 업로드, 잘못된 형식, 같은 POST 재전송, DB 실패 뒤 파일 잔존을 각각 확인한다. 원본 압축 파일을 참고할 때도 먼저 의존성과 업로드 경로를 검토하고 별도 개발 환경에서만 다룬다.

참고: Jakarta Servlet 6.1, OWASP 파일 업로드, OWASP 입력 검증.

같은 카테고리의 글