본문 바로가기
교육/Spring

Spring 개발자 수업 100일차 - 코드 게시판 만들기(글 보기, 글 쓰기)

by yhyuk 2021. 8. 19.
728x90
반응형

코드 게시판 만들기(글 보기, 글 쓰기)

[ 구현 목표 ]

- 게시판 로그인/로그아웃 구현

- 게시판 글 쓰기

- 게시판 글 목록 보기

왼쪽: 글 리스트 보기, 오른쪽: 글 쓰기

 

[ 테이블 구조 ]

유저, 댓글, 코드, 언어, 연결 테이블 총 5개

 

[ 설정 ]

스프링을 이용한 코드 게시판 만들기
    - Spring MVC Project + MyBatis + Log4j + AOP + Tiles
    - 게시판(+소스 코드 첨부) 

1. pom.xml
    - java, spring 버전 변경(1.11, 4.3.8)
    - jar(lombok, jdbc, mybatis, log4j, tiles)

2. web.xml
    - 확장자 설정(*.action)
    - POST 인코딩 필터(UTF-8)

3. root-context.xml
    - myBatis 설정
        > mapper 설정: com.test.code.mapper > code.xml
        > xml 설정: WEB-INF > myBatis.xml 
    - log4j 설정
        > <bean>태그 추가
        > log4j.xml 수정

4. servlet-context.xml
    - tiles 설정(tiles.xml)
    - AOP 설정(aop태그 추가)

5. 파일 생성
    - Java파일(com.test.code)
        > CodeController.java       : 컨트롤러
        > CodeDAO.java              : DAO
        > CodeDTO.java              : 게시판
        > CodeLanguageDTO.java      : 카테고리
        > CodeLanguageLinkDTO.java  : 게시판 <-> 카테고리
        > CodeCommentDTO.java       : 댓글
    - jsp파일(views)
        > add.jsp    : 글 쓰기
        > list.jsp   : 글 리스트
        > view.jsp   : 글 상세
        > index.jsp  : 홈 화면

6. tiles 적용
    - views > layout > template.jsp

 

[ CodeController.java ]

- 글 쓰기(add.jsp), 글 보기(list.jsp), 로그인, 로그아웃 구현하기

@Controller
public class CodeController {
	
	@Autowired
	private CodeDAO dao;
	
	// 홈 화면
	@RequestMapping(value = "/index.action", method = { RequestMethod.GET })
	public String index (HttpServletRequest req, HttpServletResponse resp, HttpSession session) {

		return "index";
	}
    
	// 로그인
	@RequestMapping(value = "/signin.action", method = { RequestMethod.POST })
	public void signin (HttpServletRequest req, 
						HttpServletResponse resp, 
						HttpSession session,
						String pw) {

		// 1. 데이터 가져오기(pw)
		
		// 2. DB 작업 > select
		String id = dao.signin(pw);
		
		// 3. 인증 티켓 발급
		if (id != null) {
			session.setAttribute("id", id); // 인증 티켓 발급(로그인)
		}
		
		// 4. 결과 처리
		try {
			resp.sendRedirect("/code/index.action");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}
	
	// 로그아웃
	@RequestMapping(value = "/signout.action", method = { RequestMethod.GET })
	public void signout (HttpServletRequest req, HttpServletResponse resp, HttpSession session) {
		
		session.removeAttribute("id"); // 인증 티켓 제거(로그아웃)
		
		try {
			resp.sendRedirect("/code/index.action");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}    

	// 글 보기
	@RequestMapping(value = "/list.action", method = { RequestMethod.GET })
	public String list (HttpServletRequest req, 
						HttpServletResponse resp, 
						HttpSession session,
						String lseq) {

		List<CodeDTO> list = dao.list(lseq);
		
		for (CodeDTO dto : list) {
			// 게시물 1개 > 글 번호 조건 > SQL > 언어 목록
			List<CodeLanguageDTO> llist = dao.llist(dto.getSeq());
			dto.setLlist(llist);
			
		}
		
		req.setAttribute("list", list);
		
		return "list";
	}

	// 글 쓰기
	@RequestMapping(value = "/add.action", method = { RequestMethod.GET })
	public String add (HttpServletRequest req, HttpServletResponse resp, HttpSession session) {

		List<CodeLanguageDTO> llist = dao.llistAll();
		req.setAttribute("llist", llist);
		
		return "add";
	}

	// 글 쓰기 
	@RequestMapping(value = "/addok.action", method = { RequestMethod.POST })
	public void addok (HttpServletRequest req, 
						HttpServletResponse resp, 
						HttpSession session,
						CodeDTO dto,
						String lseq) {
		
		// 1. 데이터 가져오기(subject, content, code, lseq)
		// 2. DB 작업
		// 	- 게시물 insert x 1
		//	- 언어(링크) insert x N > lseq

		dao.add(dto);
		
		// 방금 작성한 게시물 번호 가져오기
		String seq = dao.getSeq();
		
		// 히든 태그로 넘겨받은 N개의 lseq(언어번호)
		String[] temp = lseq.split(",");
		
		CodeLanguageLinkDTO lldto = new CodeLanguageLinkDTO();
		lldto.setCseq(seq);
		
		for (String llseq : temp) {
			// 선택한 언어 번호(llseq) + 방금 작성한 게시물 번호(seq)
			lldto.setLseq(llseq);
			dao.addLink(lldto);
		}
		
		try {
			resp.sendRedirect("/code/list.action");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}


}

 

[ Code.DAO ]

- 반환값이 1개일 때 selectOne 반환

- 반환값이 N개일 때 selectList 반환

@Repository
public class CodeDAO {

	@Autowired
	private SqlSessionTemplate template;

	// 로그인 확인 작업
	public String signin(String pw) {
		return template.selectOne("code.signin", pw);
	}
	
	// 전체 글 보기 작업
	public List<CodeDTO> list(String lseq) {
		return template.selectList("code.list", lseq);
	}

	// 특정 언어 글 보기 작업
	public List<CodeLanguageDTO> llist(String seq) {
		return template.selectList("code.llist", seq);
	}

	// 모든 언어 글 보기 작업
	public List<CodeLanguageDTO> llistAll() {
		return template.selectList("code.llistAll");
	}

	// 글 쓰기 작업
	public void add(CodeDTO dto) {
		template.insert("code.add", dto);
	}

	// 방금 쓴 글 seq 가져오는 작업
	public String getSeq() {
		return template.selectOne("code.getSeq");
	}

	// 방금 쓴 글과 연결된 언어 추가
	public void addLink(CodeLanguageLinkDTO lldto) {
		template.insert("code.addLink", lldto);
	}

}

 

[ DTO ]

- Code, CodeComment, CodeLanguage, CodeLanguageLink

// Code.DTO
@Data
public class CodeDTO {
	
	private String seq;
	private String subject;
	private String content;
	private String code;
	private String regdate;
	private List<CodeLanguageDTO> llist; // 현재 게시물에 관련된 언어 목록

}

// CodeComment.DTO
@Data
public class CodeCommentDTO {
	
	private String seq;
	private String content;
	private String beginline;
	private String endline;
	private String regdate;
	private String cseq;

}

// CodeLanguage.DTO
@Data
public class CodeLanguageDTO {
	
	private String seq;
	private String name;
	private String langicon;
	private String langclass;
	private String color;
	private String cseq; // outer join용

}

// CodeLanguageLink.DTO
@Data
public class CodeLanguageLinkDTO {

	private String seq;
	private String cseq;
	private String lseq;
}

 

[ mapper ]

- com.test.code.mapper > code.xml

- 쿼리 작성

<mapper namespace="code">

	<!-- 로그인 -->
	<select id="signin" parameterType="String" resultType="String">
		select id from tblCodeUser where id='hong' and pw=#{pw}
	</select>
	
	<!-- 게시물 -->
	<select id="list" resultType="dto" parameterType="String">
		<!-- select * from tblCode order by seq desc; -->
		
		select * from tblCode c
			<!-- MyBatis의 동적 쿼리이며 String은 예외발생이 있으므로 일종의 예약어인 언더바(_)를 붙인다.  -->
			<if test="_parameter != null"> 
			inner join tblCodeLanguageLink cll
        		on c.seq = cll.cseq
            		where cll.lseq = #{ lseq }
			</if>
				order by c.seq desc
	</select>
	
	<!-- 특정 게시물에 관련된 언어 목록 -->
	<select id="llist" parameterType="String" resultType="ldto">
		select * from tblCodeLanguage cl
    		left outer join (select * from tblCodeLanguageLink where cseq = #{ seq }) cll
        		on cl.seq = cll.lseq
        			order by cl.seq asc
	</select>
	
	<!-- 모든 언어 -->
	<select id="llistAll" resultType="ldto">
		select * from tblCodeLanguage order by seq asc
	</select>
	
	<!-- 글 쓰기 -->
	<insert id="add" parameterType="dto">
		insert into tblCode (seq, subject, content, code, regdate)
			values (seqCode.nextVal, #{ subject }, #{ content }, #{ code }, default)
	</insert>
	
	<!-- 방금 쓴 글의 번호 -->	
	<select id="getSeq" resultType="String">
		select max(seq) from tblCode
	</select>
	
	<!-- 방금 쓴글과 연결된 언어 추가  -->
	<insert id="addLink" parameterType="cldto">
		insert into tblCodeLanguageLink (seq, cseq, lseq) 
			values (seqCodeLanguageLink.nextVal, #{ cseq }, #{ lseq })
	</insert>
	
</mapper>

 

[ index.jsp ]

- 홈 페이지

- 로그인/로그아웃 및 코드 게시판으로 이동하는 기능을 수행 한다.

<!-- 로그인 안했을 때 -->
<c:if test="${ empty id }">
	<form method="POST" action="/code/signin.action">
		<!-- 전송할때 text하나면 submit 안만들어도됨.(엔터만 치면됨) -->
		<input type="password" name="pw" class="form-control" required autofocus placeholder="passoword">
	</form>        
</c:if>

<!-- 로그인 했을 때 -->
<c:if test="${ not empty id }">
	<!-- 코드 게시판 이동 -->
	<div onclick="location.href='/code/list.action';" class="signed">signed. Code</div>
	<!-- 로그아웃 -->
	<div onclick="location.href='/code/signout.action';" class="signed">sign out.</div>
</c:if>

 

[ add.jsp ]

- 글 쓰기 페이지

- 글 제목, 코드 설명 내용, 코드 내용, javascript로 적용한 코드 내용 언어 설정을 할 수 있다.

<form method="POST" action="/code/addok.action">        
<table class="table table-bordered table-add">
    <tr>
        <td><input type="text" name="subject" class="form-control" required placeholder="Subject" autocomplete="off"></td>
    </tr>
    <tr>
        <td><textarea name="content" class="form-control" required placeholder="Description"></textarea></td>
    </tr>
    <tr>
        <td>
            <div class="language">
            
            	<c:forEach items="${llist}" var="ldto">
	            	<i class="${ldto.langclass} ${ldto.langicon} icon off"
	    			   title="${ldto.name}" 
	    			   data-toggle="tooltip" 
	    			   data-placement="bottom" 
	    			   data-language="${ldto.name.toLowerCase()}"
	    			   data-lseq="${ldto.seq}"></i>
                </c:forEach>
                
            </div>
        </td>
    </tr>
    <tr>
        <td><textarea name="code" class="form-control" required placeholder="Code"></textarea></td>
    </tr>
</table>


<div class="btns">
    <button type="button" class="btn btn-default" onclick="location.href='/code/list.action';">Back</button>
          <button type="submit" class="btn btn-primary">Create Code</button>
      </div>

<input type="hidden" name="lseq">

</form>

<script src='/code/resources/js/add.js'></script>

 

[ list.jsp ]

- 글 리스트 페이지

- 해당 글을 클릭하면 상세페이지로 이동한다.

- 코드 언어 아이콘을 클릭하면 해당 언어와 관련된 언어 리스트 목록을 확인 할 수 있다.

<c:forEach items="${list}" var="dto">
<div class="item">
    <div class="language">
    	
    	<c:forEach items="${ dto.llist }" var="ldto">
    		<!-- 관련 언어 O -->
    		<c:if test="${ not empty ldto.cseq }">
    			<i class="${ ldto.langclass} ${ldto.langicon } icon"
    			   title="${ ldto.name }" 
    			   data-toggle="tooltip" 
    			   data-placement="bottom" 
    			   data-language="${ldto.name.toLowerCase() }" 
    			   onclick="location.href='/code/list.action?lseq=${ ldto.seq }';"></i>
    		</c:if>
    		
    		<!-- 관련 언어 X -->
    		<c:if test="${ empty ldto.cseq }">
    			<i class="empty-icon icon"></i>
    		</c:if>
        
        </c:forEach>
        
    </div>
    <div class="desc" onclick="location.href='/code/view.action';">
        <h4>${ dto.subject }</h4>
        <div class="ellipsis">${ dto.content }</div>
    </div>
</div>
</c:forEach>



<div class="btns">
    <button type="button" class="btn btn-default" onclick="location.href='/code/list.action';">All Code</button>
    <button type="button" class="btn btn-primary" onclick="location.href='/code/add.action';">Add Code</button>
</div>



<script src='/code/resources/js/list.js'></script>

 

[ 구현 화면 ]

index, list, add

 


MEMO>

# 일반 게시판 CRUD 구현이 아닌, 코드 작성과 설명을 작성하는 게시판 구현은 처음하다니 보니 낯설어서 많이 헷갈렸다. 다시 복습할 것

# 오늘은 쓰기, 보기 구현을 했으니 내일은 상세 보기 구현!!

# 마지막 프로젝트 + 취업 준비로 많이 어수선 하지만 유종의 미를 거둘 수 있게 노력하기!
728x90
반응형

댓글