왜 갑자기 노래를 넣느냐고 물어본다면 대답해주는 게 인지상정.

한창 프로젝트하던 시절 이 노래를 제일 많이 들었거든요 (아련)

오늘의 포스팅 내용은 .. 바로바로 ~ 게시판 부분 ~!

제가 맡았던 게시판은 공지사항과 FAQ입니다만, 오늘은 그 중에서도 공지사항 부분을 말씀드리겠습니다. φ(゜▽゜*)♪

사유 : FAQ는 몽고DB임.

제가 이런 포스팅을 본 적도 없고 쓰는 것도 처음이라 갠잔히 어색할 수 있는데 잘 봐주십쇼 얄루 ~


six ..

최종 진화 형태의 저희 프로젝트입니다.

참고로 브랜치 이름이 저런 이유는 .. 처음에는 소스트리를 쓰다가 나중에 맛이 가서 계속 하나씩 만들다보니깐 저렇게 됐읍니다.

공지사항에서 구현한 기능으로는 기본적으로 CRUD / 글 고정 / 첨부파일 업로드, 다운로드 기능이 있습니다.

공지사항 폴더 안
(오라클) 공지사항 테이블

실제 배포 시점에서는 네이버 서버를 이용하다보니 Mysql로 변경하였었습니다.

NOTICE_NO는 프라이머리키로 줬고요, NOTICE_TITLE과 NOTICE_CONTENT는 글 내용입니다.

MANAGER_ID는 공지사항을 적는 관리자 아이디고 유저가 보는 화면에서는 보이지 않게 하였습니다. 글 작성할 때는 자동으로 아이디를 받아오게하였고요 !

NOTICE_WRITEDATE는 글을 작성하는 날짜이며, NOTICE_FIX는 글 공지 여부입니다.

(오라클) 공지사항 데이터

제 pc 안에 저장돼있는 글들입니다.

 

제일 먼저 NoticeDTO를 보여드리겠습니다.

NoticeFileDTO고요 ~~

package com.project.notice;

public class NoticeFileDTO {
		private String Notice_no;
		private String originalFilename;
		private String storeFilename;
		private String NoticeFileno;
		
		private NoticeFileDTO() {
		}
		
		public NoticeFileDTO(String originalFilename, String storeFilename) {
			super();
			this.originalFilename = originalFilename;
			this.storeFilename = storeFilename;
		}
		//Noticefile의 상세정보 - insert
		public NoticeFileDTO(String originalFilename, String storeFilename, String NoticeFileno) {
			super();
			this.originalFilename = originalFilename;
			this.storeFilename = storeFilename;
			this.NoticeFileno = NoticeFileno;
		}
		//select
		public NoticeFileDTO(String Notice_no, String originalFilename, String storeFilename, String NoticeFileno) {
			super();
			this.Notice_no = Notice_no;
			this.originalFilename = originalFilename;
			this.storeFilename = storeFilename;
			this.NoticeFileno = NoticeFileno;
		}

		@Override
		public String toString() {
			return "NoticeFileDTO [Notice_no=" + Notice_no + ", originalFilename=" + originalFilename + ", storeFilename="
					+ storeFilename + ", NoticeFileno=" + NoticeFileno + "]";
		}

		public String getNotice_no() {
			return Notice_no;
		}
		public void setNotice_no(String Notice_no) {
			this.Notice_no = Notice_no;
		}
		public String getOriginalFilename() {
			return originalFilename;
		}
		public void setOriginalFilename(String originalFilename) {
			this.originalFilename = originalFilename;
		}
		public String getStoreFilename() {
			return storeFilename;
		}
		public void setStoreFilename(String storeFilename) {
			this.storeFilename = storeFilename;
		}

		public String getNoticeFileno() {
			return NoticeFileno;
		}

		public void setNoticeFileno(String NoticeFileno) {
			this.NoticeFileno = NoticeFileno;
		}
		
	}

 

위에 DB에 있는 테이블들을 불러와줬습니다.

 

NoticeDAO입니다.

package com.project.notice;

import java.util.List;

import com.project.file.BoardFileDTO;


public interface NoticeDAO {
    //게시글등록 - db에 처리
    int insert(NoticeDTO noticeBoard);
    //게시글목록보기 - db에 처리
    List<NoticeDTO> noticeList();
    //게시글상세조회 - db에 처리
    NoticeDTO read(String notice_no);
    //게시글수정 - db에 처리
    int update(NoticeDTO noticeBoard);
    //게시글삭제 - db에 처리
    int delete(String notice_no);
    //제목으로 검색
    List<NoticeDTO> search(String data);
    //제목,작성자, 본문, 작성일별로 검색
    List<NoticeDTO> search(String tag,String data);

    // *************** 첨파 ***************
    //첨부파일을 저장하기 위한 메소드
    int insertFile(List<BoardFileDTO> boardfiledtolist);
//    List<BoardFileDTO> getFileList(String notice_no);
//    BoardFileDTO getFile(String inputdata);

}

 

기본적으로 글 작성, 리스트, read 화면, 수정, 삭제 등을 두었고 그 외에 몇 가지 기능들이 보이는데

실제 공지사항 화면에서는 전부 다 구현하진 않았습니다.

 

이어서 NoticeDAOImpl입니다.

package com.project.notice;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.project.file.BoardFileDTO;

@Repository
public class NoticeDAOImpl implements NoticeDAO {
	
	SqlSession sqlSession;
	
	public NoticeDAOImpl() {
	}
	
	@Autowired
	public NoticeDAOImpl(SqlSession sqlSession) {
		super();
		this.sqlSession = sqlSession;
	}

	@Override
	public int insert(NoticeDTO noticeBoard) {
		return sqlSession.insert("com.project.notice.write", noticeBoard);
	}
	
	@Override
	public NoticeDTO read(String notice_no) {
		NoticeDTO dto = sqlSession.selectOne("com.project.notice.read",notice_no);
			dto.setNotice_writedate(dto.getNotice_writedate().substring(0,19));
		return dto;
	}

	@Override
	public int update(NoticeDTO noticeBoard) {
		return sqlSession.update("com.project.notice.update",noticeBoard);
	}
	
	@Override
	public int delete(String notice_no) {
		return sqlSession.delete("com.project.notice.delete",notice_no);
	}
	
	@Override
	public List<NoticeDTO> search(String data) {
		return sqlSession.selectList("com.project.notice.search",data);
	}
	
	@Override
	public List<NoticeDTO> search(String tag, String data) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("tag", tag);
		map.put("data", data);
		return sqlSession.selectList("com.project.notice.dynamicSearch", map);
	}
	
	@Override
	public List<NoticeDTO> noticeList() {
		List<NoticeDTO> list = sqlSession.selectList("com.project.notice.selectall");
		for(int i=0; i<list.size();i++) {
			list.get(i).setNotice_writedate(list.get(i).getNotice_writedate().substring(0,19));
		}
		return  list;
	}

	@Override
	public int insertFile(List<BoardFileDTO> boardfiledtolist) {
		return sqlSession.insert("com.project.notice.notice_fileinsert", boardfiledtolist);
	}


//	@Override
//	public List<BoardFileDTO> getFileList(String notice_no) {
//		return sqlSession.selectList("com.project.notice.fileselect", notice_no);
//	}
//
//	@Override
//	public BoardFileDTO getFile(String inputdata) {
//		return sqlSession.selectOne("com.project.notice.getfileinfo", inputdata);
//
//	}

}

mapper은은 뒤쪽에서 확인 가능합니다 ~ 

 

NoticeService입니다.

package com.project.notice;

import java.util.List;

import org.springframework.stereotype.Service;

import com.project.file.BoardFileDTO;

@Service
public interface NoticeService {
		//게시글등록 - tbNotice테이블과 Notice_file테이블에 저장
	int insert(NoticeDTO Notice, List<BoardFileDTO> boardfiledtolist);
	//게시글 그냥 등록
	int insert(NoticeDTO Notice);
	//게시글목록보기
	List<NoticeDTO> noticeList();
	//게시글상세조회
	NoticeDTO getNoticeInfo(String Notice_no);
	//게시글수정
	int update(NoticeDTO Notice);
	//게시글삭제
	int delete(String notice_no);
	//제목으로 검색
	List<NoticeDTO> search(String data);
	//제목,작성자, 본문, 작성일별로 검색
	List<NoticeDTO> search(String tag,String data);
	//게시글을 상세보기한 경우 보여질 업로드한 파일의 목록
//	List<BoardFileDTO> getFileList(String boardno);
//	BoardFileDTO getFile(BoardFileDTO inputdata);
}

 

이어서 NoticeServiceImpl입니다.

package com.project.notice;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.project.file.BoardFileDTO;

@Service
public class NoticeServiceImpl implements NoticeService{
	NoticeDAO dao;
	
	public NoticeServiceImpl() {
	}
	
	@Autowired
	public NoticeServiceImpl(NoticeDAO dao) {
		super();
		this.dao = dao;
	}

	@Override
	public List<NoticeDTO> noticeList() {
		return dao.noticeList();
	}

	@Override
	public NoticeDTO getNoticeInfo(String notice_no) {
		return dao.read(notice_no);
	}

	@Override
	public int update(NoticeDTO notice) {
		return dao.update(notice);
	}

	@Override
	public int delete(String notice_no) {
		return dao.delete(notice_no);
	}

	@Override
	public List<NoticeDTO> search(String data) {
		return dao.search(data);
	}

	@Override
	public List<NoticeDTO> search(String tag, String data) {
		return dao.search(tag, data);
	}
	
	@Override
	public int insert(NoticeDTO Notice, List<BoardFileDTO> boardfiledtolist) {
		dao.insert(Notice);
		dao.insertFile(boardfiledtolist);
		return 0;
	}

	@Override
	public int insert(NoticeDTO Notice) {
		dao.insert(Notice);
		return 0;
	}

//	@Override
//	public BoardFileDTO getFile(BoardFileDTO inputdata) {
//		// TODO Auto-generated method stub
//		return /*dao.getFile(inputdata);*/ null;
//	}
//
//	@Override
//	public List<BoardFileDTO> getFileList(String notice_no) {
//		return dao.getFileList(notice_no);
//	}

}

 

NoticeController입니다.

package com.project.notice;

import java.io.IOException;
import java.util.List;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.WebUtils;

import com.project.file.BoardFileDTO;
import com.project.file.BoardFileService;
import com.project.file.FileUploadLogic;

@Controller
public class NoticeController {
	FileUploadLogic fileuploadService;
	NoticeService service;
	BoardFileService boardservice;

	public NoticeController() {
	}

	@Autowired
	public NoticeController(NoticeService service, FileUploadLogic fileuploadService, BoardFileService boardservice) {
		super();
		this.service = service;
		this.fileuploadService = fileuploadService;
		this.boardservice = boardservice;
	}

	// insert 페이지로 이동
	@RequestMapping(value = "/service/noticeinsert", method = RequestMethod.GET)
	public String insert() {
		return "service_noticeinsert";
	}

	@RequestMapping(value = "/service/noticeinsert", method = RequestMethod.POST)
	public String insert(NoticeDTO Notice, HttpSession session) throws IllegalStateException, IOException {
		System.out.println("Notice=>" + Notice);
		// 1. MultipartFile 정보를 추출하기
		List<MultipartFile> files = Notice.getFiles();
		// 2. 업로드될 서버의 경로 - 실제 서버의 경로를 추출하기 위해서 context의 정보를 담고 있는 ServletContext객체를 추출
		// =>getServletContext는 우리가 생성한 프로젝트가 서버에 배포되는 실제 경로와 context에 대한 정보를 담고 있는 객체
		String path = WebUtils.getRealPath(session.getServletContext(), "/WEB-INF/upload");
		// System.out.println(path);
		// 3. 파일업로드 서비스를 호출해서 실제 서버에 업로드되도록 작업하기

		List<BoardFileDTO> boardfiledtolist = fileuploadService.uploadFiles(files, path);
		// 업로드된 파일의 file_no의 값을 셋팅 - 1부터 1,2,3,4....첨부파일마지막번호

		// System.out.println("boardfiledtolist???????? " + boardfiledtolist);

		// 4. 게시글에 대한 일반적인 정보와 첨부되는 파일의 정보를 db에 저장하기
		if (boardfiledtolist.isEmpty()) {
			service.insert(Notice);
		} else {
			int count = 1;
			for (BoardFileDTO boardfiledto : boardfiledtolist) {
				boardfiledto.setFile_no(count + "");
				count++;
			}
			service.insert(Notice, boardfiledtolist);
		}
		return "redirect:/admin_notice.do";
	}

	@RequestMapping("/Notice/list.do")
	public ModelAndView list() {
		ModelAndView mav = new ModelAndView("service_notice");
		List<NoticeDTO> noticelist = service.noticeList();
		mav.addObject("noticelist", noticelist);
		return mav;
	}

	@RequestMapping("/admin_notice.do")
	public ModelAndView list2() {
		ModelAndView mav = new ModelAndView("admin_noticelist");
		List<NoticeDTO> noticelist = service.noticeList();
		mav.addObject("noticelist", noticelist);
		return mav;
	}

	// User notice read
	@RequestMapping("/notice/read.do")
	public String read(String notice_no, String state, Model model) {
		NoticeDTO notice = service.getNoticeInfo(notice_no);
		List<BoardFileDTO> boardfiledtolist = boardservice.getFileListNo(notice_no);
		System.out.println("공지사항boardfiledtolist: " + boardfiledtolist);
		String view = "";
		if (state.equals("READ")) {
			view = "service_noticeread";
		} else {
			view = "service_noticeupdate";
		}
		model.addAttribute("notice", notice);
		model.addAttribute("boardfiledtolist", boardfiledtolist);
		return view;
	}

	// admin notice read
	@RequestMapping("/notice/readadmin.do")
	public String read2(String notice_no, String state, Model model) {
		NoticeDTO notice = service.getNoticeInfo(notice_no);
		List<BoardFileDTO> boardfiledtolist = boardservice.getFileListNo(notice_no);
		// System.out.println("공지사항boardfiledtolist: "+boardfiledtolist);
		String view = "";
		if (state.equals("READ")) {
			view = "service_noticereadadmin";
		} else {
			view = "service_noticeupdate";
		}
		model.addAttribute("notice", notice);
		model.addAttribute("boardfiledtolist", boardfiledtolist);
		return view;
	}

	// delete를 시도하면 로그인 유무를 체크해서 로그인을 하지 않은 사용자는 로그인을 할 수 있도록 로그인페이지로 리다이렉트
	@RequestMapping("/notice/delete.do")
	public String delete(String notice_no, HttpSession session) {
//		MemberDTO user = (MemberDTO) session.getAttribute("user");
		String view = "";
//		if(user==null) { //로그인 하지 않은 상태
//			 view = "redirect:/emp/login.do";
//		}else { //로그인 성공 상태
//			int result = service.delete(Notice_no);
//			view = "redirect:/Notice/list.do?category=all";
//		}
		service.delete(notice_no);
		view = "redirect:/admin_notice.do";
		return view;
	}

	// 실제 업데이트기능을 처리
	@RequestMapping("/notice/update.do")
	public String update(NoticeDTO noticeboard) {
		System.out.println(noticeboard + "-----------업데이트---------------------");
		service.update(noticeboard);
		return "redirect:/admin_notice.do";
	}

	@RequestMapping("/notice/search.do")
	public ModelAndView search(String tag, String data) {
		ModelAndView mav = new ModelAndView("notice/list");
		List<NoticeDTO> Noticelist = service.search(tag, data);
		System.out.println(Noticelist);
		mav.addObject("Noticelist", Noticelist);
		return mav;
	}

}

 

마지막으로 FileUploadLogic입니다.

package com.project.notice;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service("notice")
public class FileUploadLogic {
	//파일을 업로드한 후 이 정보를 NoticeFileDTO로 변환해서 리턴
	public List<NoticeFileDTO> uploadFiles(List<MultipartFile> multipartFiles, String path) throws IllegalStateException, IOException{
		List<NoticeFileDTO> filedtolist = new ArrayList<NoticeFileDTO>();
		for(MultipartFile MultipartFile : multipartFiles) {
			if(!MultipartFile.isEmpty()) {
				String originalFilename = MultipartFile.getOriginalFilename();
				//서버에서 식별할 수 있도록 파일명을 변경
				String  storeFilename = createStoreFilename(originalFilename);
				System.out.println(originalFilename);
				//File객체를 실제 경로에 저장
				MultipartFile.transferTo(new File(path+File.separator+storeFilename));
				filedtolist.add(new NoticeFileDTO(originalFilename, storeFilename));
			}
		}
		return filedtolist;
	}
	//uploadFile메소드를 작성
	// -> 한 개의 파일만 업로드 할 수 있도록 정의
	// -> uploadFiles메소드에서 작성한 uploadFile메소드를 호출해서 작업하기
	//클라이언트가 입력한 파일명을 중복 없는 이름으로 변경
	//UUID- 32자리의 16진수로 표기
	private String createStoreFilename(String originalFilename) {
		int pos = originalFilename.lastIndexOf(".");
		String ext = originalFilename.substring(pos+1);
		String uuid = UUID.randomUUID().toString();
		return uuid+"."+ext;
	}
}

 

mapper에 있는 noticeboard.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.project.notice">

 <!--	<select id="fileselect" parameterType="String" resultType="boardfile">
		select * from file_tb where notice_no = #{notice_no}
	</select>
	 <select id="getfileinfo" parameterType="boardfile" resultType="boardfile">
		select * from file_tb where notice_no = #{notice_no} and file_no =#{file_no}
	</select> -->
	
	<insert id="write" parameterType="notice">
		insert into NOTICE_TB 
		values(null,#{notice_title},#{notice_content},#{manager_id},now(), #{notice_fix})
	</insert>
	<select id="selectall" resultType="notice">
		select * from NOTICE_TB order by notice_writedate desc
	</select>
	<insert id="notice_fileinsert" parameterType="java.util.List">
		insert into FILE_TB values 
		<foreach collection="list" item="file" separator="">
			(#{file.file_no}, 0, #{file.storeFilename}, #{file.originalFilename}, '공지사항', last_insert_id())
		</foreach>
	</insert>
	<select id="read" resultType="notice" parameterType="String">
		select * from NOTICE_TB where notice_no = #{notice_no}
	</select>
	<delete id="delete" parameterType="String">
		delete from NOTICE_TB where notice_no = #{notice_no}
	</delete>
	<update id="update" parameterType="notice">
		update NOTICE_TB
		set notice_title=#{notice_title},notice_content=#{notice_content}
		where notice_no=#{notice_no}
	</update>
	
	<!-- 검색기능 -->
	<select id="search" resultType="notice" parameterType="String" >
		select * from NOTICE_TB where title like  concat('%',#{notice_title},'%')
	</select>
	<select id="dynamicSearch" resultType="notice" parameterType="Map">
		select * from NOTICE_TB
		<where>
			<if test="tag=='title' and data!=''">
			title like concat('%',#{data},'%')
			</if>
			<if test="tag=='id' and data!=''">
			id like concat('%',#{data},'%')
			</if>
			<if test="tag=='content' and data!=''">
			content concat('%',#{data},'%')
			</if>
			<if test="tag=='notice_writedate' and data!=''">
				<![CDATA[
					notice_writedate <= #{data}
				]]>
				</if>
		</where>
	</select>
</mapper>

 

내일은 공지사항 프론트 부분으로 오겠습니다 ~! (´▽`ʃ♡ƪ)

총 7일 정도로 기획하고 작성하는 개발자 과정 내용.

대학생 시절에 공부하던 내용 정리하던 네이버 블로그가 있긴 한데, 멋들어지게 한 번 만들어봤습니다. 티스토리.

 

주제는 제주도 전기차 충전소 현황을 볼 수 있는 사이트

 

GitHub - multiFinal4/final

Contribute to multiFinal4/final development by creating an account on GitHub.

github.com

좀 더 상세하게 보고 싶으신 분들은 링크에 가서 보시길 !

 

팀원은 모두 똑같은 과정을 수강했던 학원 멤버 5명이고,

총 개발 과정은 6명이고 마지막 2주 정도는 매일 새벽 00:30 ~ 2:00까지,

주말 및 공휴일에도 되도록 모여서 했었습니다. (크리스마스 및 설 명절도 반납)

 

프로젝트 이후에도 마음 맞는 저희 조원 5명은 스터디도 시작했고요 ~!

위 프로젝트를 주제로 설명하는 6번의 포스팅이 있으니 많관부탁드려요 ~ (^人^)


4조_파이널프로젝트_차지모양_발표PPT.pdf
4.47MB

+ Recent posts