개발환경

  • MacBook Air (M1, 2020)
  • OpenJDK 8
  • Eclipse 2021-12
  • tomcat 8.5
  • MySQL Workbench 8.0.19


기간

  • 2022.3.4 ~ 2022.4.6


주제

  • 웹 백엔드 수업 중 중간 과제로 개인 프로젝트를 진행하게 되었다.
  • 회원가입/로그인/탈퇴 등 기본적인 회원관리 시스템을 가진 웹 사이트를 만드는 것이다. 주어진 기한은 한 달
  • 나는 다음 카페를 소규모로 만들어 보기로 했다. 평소 자주 이용하기도 했고 과제의 평가 기준에서 요구하는 기능들을 다 담고 있기도 했기 때문에 이번 기회에 구현해 보면 그동안 배운 것들을 활용하기에 좋을 거 같았다.
  • 평가 기준에 사이트의 디자인 구현(HTML/CSS 등 프론트엔드)은 포함되지 않기 때문에 본인이 쓰고 싶은 HTML/CSS 템플릿을 구한 뒤 회원 관리 기능을 구현하면 된다.


진행상황

  • 게시글에 첨부된 이미지가 있으면 메인 화면에서 게시글 내용을 미리보기로 보여줄 때 이미지도 함께 보여주는 기능을 추가했다.
  • 원본 이미지로 썸네일용 이미지를 만들어주는 Thumbnailator 라이브러리를 사용했다.

Thumbnailator 라이브러리 다운로드 및 설치

BoardWriteAction.java

package com.project.cafe.board.action;

import java.io.File;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.multipart.FilePart;
import com.oreilly.servlet.multipart.MultipartParser;
import com.oreilly.servlet.multipart.ParamPart;
import com.oreilly.servlet.multipart.Part;
import com.project.cafe.action.Action;
import com.project.cafe.action.ActionForward;
import com.project.cafe.board.db.BoardDAO;
import com.project.cafe.board.db.BoardDTO;

import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.name.Rename;

public class BoardWriteAction implements Action 
{
    @Override
    public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception 
    {
        // .. 생략
		
        while ((part = mp.readNextPart()) != null)
        {
            //.. 생략
            
            else if (part.isFile() && name.equals("image"))
            {
                // 이미지 파일일 때
                // 이미지 저장 경로 지정
                File dir = new File(ctx.getRealPath("/images"));
                File originDir = new File(dir + File.separator + "origins"); // 원본 이미지 저장 경로
                File thumbnailDir = new File(dir + File.separator + "thumbnails"); // 썸네일 저장 경로
				
                // 경로가 없으면 생성
                if (!dir.isDirectory()) dir.mkdir();
                if (!originDir.isDirectory()) originDir.mkdir();
                if (!thumbnailDir.isDirectory()) thumbnailDir.mkdir();
				
                FilePart filePart = (FilePart) part;
                String file = filePart.getFileName();
                if (file != null)
                {
                    // 지정한 경로에 원본 이미지 파일 쓰기
                    filePart.writeTo(originDir);
                    dto.setImage(file);
					
                    // 원본 이미지 파일을 이용해서 썸네일을 만들어 저장
                    // 크기 지정 후 지정 경로에 저장
                    Thumbnails.of(new File(originDir.getPath() + File.separator + file))
                        .size(300, 400)
                        .toFiles(thumbnailDir, Rename.NO_CHANGE);
					
                    System.out.println("img name: "+file);
                }
                else 
                    System.out.println("image; name: " + name + "; EMPTY");
            }
            // .. 생략
        }
		
        // .. 생략
		
        return forward;
    }
}
  • 새 게시글을 작성하는 서블릿에서 원본 이미지 외에도 썸네일을 만들어 저장하는 코드를 추가했다.
  • 원본 이미지와 썸네일을 저장할 폴더를 각각 구분해서 업로드한다.

BoardImgAction.java

package com.project.cafe.board.action;

import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.project.cafe.action.Action;
import com.project.cafe.action.ActionForward;

public class BoardImgAction implements Action 
{
    @Override
    public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception 
    {
        System.out.println("M : BoardImgAction - execute() 호출");
		
        // 전달된 이미지 파일 이름 저장
        String originImgName = request.getParameter("img_name");
        String thumbnailName = request.getParameter("thumbnail");
        String imgName = null;
		
        // 서버에 업로드 된 폴더 찾기
        String savePath = null;
        if (originImgName != null)
        {
            savePath = "images/origins";
            imgName = originImgName;
        }
        else 
        {
            savePath = "images/thumbnails";
            imgName = thumbnailName;
        }
		
        // 서버에 업로드 된 파일 위치 계산
        ServletContext ctx = request.getServletContext();
        String downloadPath = ctx.getRealPath(savePath);
        System.out.println("download path: "+downloadPath);
		
        // 다운로드 할 이미지의 전체 경로 계산
        // 사용자 OS에 따라 연결자 구분
        String imgPath = downloadPath + File.separator + imgName;
        System.out.println("imgPath: "+imgPath);
		
        //.. 이하 생략
    }
}

main.jsp

<section data-theme="_bgp">
  <div data-layout="_r" class="MOD_ARTICLEBLOCKS1">
    <div data-layout="al16 ec8" class="MOD_ARTICLEBLOCKS1_Cont">
    <h2>최신글</h2>
      <a href="" class="MOD_ARTICLEBLOCKS1_BlockLarge" id="mainHref1">
        <!-- 썸네일 보여주는 부분 -->
        <img id="thumbnail1" src="" class="MOD_ARTICLEBLOCKS1_Img" role="img" aria-label="alt text">
        <div class="MOD_ARTICLEBLOCKS1_Txt">
          <h3 class="MOD_ARTICLEBLOCKS1_Title" id="mainTitle1">Article Title</h3>
          <p class="MOD_ARTICLEBLOCKS1_Category" id="mainContent1">Category</p>
        </div>
      </a>
    </div>
    <div data-layout="al16 ch8 ec4" class="MOD_ARTICLEBLOCKS1_Cont">
      <a href="" class="MOD_ARTICLEBLOCKS1_BlockSmall" id="mainHref2">
        <!-- 썸네일 보여주는 부분 -->
        <img id="thumbnail2" src="" class="MOD_ARTICLEBLOCKS1_Img" role="img" aria-label="alt text">
        <div class="MOD_ARTICLEBLOCKS1_Txt">
          <h3 class="MOD_ARTICLEBLOCKS1_Title" id="mainTitle2">Article Title</h3>
          <p class="MOD_ARTICLEBLOCKS1_Category" id="mainContent2">Category</p>
        </div>
      </a>
    </div>
    <div data-layout="al16 ch8 ec4" class="MOD_ARTICLEBLOCKS1_Cont">
      <a href="#" class="MOD_ARTICLEBLOCKS1_BlockSmall" id="mainHref3">
        <!-- 썸네일 보여주는 부분 -->
        <img id="thumbnail3" src="" class="MOD_ARTICLEBLOCKS1_Img" role="img" aria-label="alt text">
        <div class="MOD_ARTICLEBLOCKS1_Txt">
          <h3 class="MOD_ARTICLEBLOCKS1_Title" id="mainTitle3">Article Title</h3>
          <p class="MOD_ARTICLEBLOCKS1_Category" id="mainContent3">Category</p>
        </div>
      </a>
    </div>
  </div>
</section>
  • img 태그에 각각 thumbnail1, 2, 3 형식으로 id를 설정해 자바스크립트로 src를 세팅해 줄 것이다.

main.js

$(document).ready(function()
{
    getFeeds();
});

function getFeeds()
{
    $.ajax({
        type: 'POST',
        async: false,
        url: './GetFeed.bo',
        dataType: 'json',
        data: {
            'cnt': 3,
            'len': 70
        },
        success: function(data) {
            if (data != null)
            {
                for (var i = 0; i < data.length; i++)
                {
                    var titleId = '#mainTitle';
                    titleId += (i + 1);
                    $(titleId).html(data[i].title);

                    var contentId = '#mainContent';
                    contentId += (i + 1);
                    $(contentId).html(data[i].content);

                    var hrefId = '#mainHref';
                    hrefId += (i + 1);
                    $(hrefId).attr('href', './BoardContent.bo?num='+data[i].num+'&pageNum=1');
					
                    var thumbnailId = '#thumbnail';
                    thumbnailId += (i + 1);
                    if (data[i].image == '없음')
                        $(thumbnailId).attr('src', '');
                    else 
                        $(thumbnailId).attr('src', './BoardImgAction.bo?thumbnail='+data[i].image);
                }
            }
        }
    });
}
  • GetFeed.java에서 가져온 데이터 중 이미지 정보가 있을 때에만 썸네일 이미지를 출력한다.

GetFeed.java

package com.project.cafe.board.action;

import java.util.ArrayList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import com.project.cafe.action.Action;
import com.project.cafe.action.ActionForward;
import com.project.cafe.board.db.BoardDAO;
import com.project.cafe.board.db.BoardDTO;

public class GetFeed implements Action 
{
    @Override
    public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception 
    {
        System.out.println("ajax 시작_GetFeed - execute() 호출");
		
        BoardDAO dao = new BoardDAO();
        ArrayList<BoardDTO> list = dao.getPosts
            (Integer.parseInt(request.getParameter("cnt")), Integer.parseInt(request.getParameter("len")));
		
        // DB에서 가져온 데이터들을 json에 저장
        JSONArray feedList = new JSONArray();
        for (int i = 0; i < list.size(); i++)
        {
            JSONObject feed = new JSONObject();
            feed.put("num", list.get(i).getNum());
            feed.put("title", list.get(i).getTitle());
            feed.put("content", list.get(i).getContent());
            feed.put("image", list.get(i).getImage());
			
            feedList.add(feed);
        }
		
        // 클라이언트에게 데이터 보내기
        // 한글처리
        response.setCharacterEncoding("UTF-8");
        // json 데이터 넘김
        response.getWriter().print(feedList.toJSONString());
        response.getWriter().close();
		
        return null;
    }
}
  • GetFeed.java에서는 json데이터를 넘긴다.

  • 완성! 👏
  • 첨부된 이미지가 있을 때에만 썸네일을 출력하고 없을 때엔 출력하지 않는다.

출처

마감까지

  • 마감 기한이 늘어났다.
  • D-5