원지의 개발
article thumbnail
728x90

SpringMyweb(2)

게시판 구현 순서

1. 컨트롤러 생성 (화면 확인)
2. 등록 처리
3. 테이블 생성
4. DB관련 설정 (root - xml작업)

5. BoardVO 생성 (DB컬럼명과 반드시 동일하게 생성)
6. Service 구현
7. DAO 구현
8. 마이바티스 DB작업

9. 상세보기
10. 변경
11. 삭제
12. 페이징 처리

DB(MySQL) 생성 및 기본 컨트롤러, 서비스 연결

2023.02.03 - [Spring] - [Spring] ??? MySQL 설치, Spring,DB연결 ▶ JDBC, Spring-JDBC, DataSource, HikariCP, Spring-test

 

[Spring] ??? MySQL 설치, Spring,DB연결 ▶ JDBC, Spring-JDBC, DataSource, HikariCP, Spring-test

MySQL MySQL 설치 https://dev.mysql.com/downloads/installer/ MySQL :: Download MySQL Installer Select Operating System: Select Operating System… Microsoft Windows Select OS Version: All Windows (x86, 32-bit) Windows (x86, 32-bit), MSI Installer 8.0.32 2

j-won950101.tistory.com

테이블 생성

create table trip (
	tno int primary key auto_increment,
    tripdate varchar(30) not null,
    writer varchar(30) not null,
    title varchar(200) not null,
    content varchar(1000),
    hit int default 0,
    regdate timestamp default now() -- 시간타입: 기본값, 지금시간
);
  • trip 테이블 생성
  • tripdate 는 화면에서 넘어오는 날짜로 String 타입
  • regdate 는 timestamp로 디폴트 값은 현재 시간

더미 데이터 생성

package com.coding404.myweb;

import java.util.ArrayList;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.coding404.myweb.command.TripVO;
import com.coding404.myweb.trip.service.TripMapper;
import com.coding404.myweb.util.Criteria;

@RunWith(SpringJUnit4ClassRunner.class) //junit으로 테스트환경을 구성
@ContextConfiguration("file:src/main/webapp/WEB-INF/config/root-context.xml") //동작시킬 스프링 설정 파일 - 전체 풀 경로
public class PageTest {

	@Autowired
	TripMapper tripMapper;
	
//	테스트 코드로 반복문 돌려서 DB에 넣기
	@Test
	public void testCode() {
		
		for(int i = 1; i <= 300; i++) {
		TripVO vo = new TripVO(0, "2024-01-01", "admin"+i, "test"+i, "example"+i, 0, null);
		tripMapper.noticeRegist(vo);
		}
	}
	
}

Service 패키지 - 파일 생성

service, mapper 파일 생성

  • TripService 인터페이스 - TripServiceImpl 생성
  • TripMapper 인터페이스 생성
  • src/main/resources 에 sqlmap 폴더 - TripMapper.xml 생성

Controller  패키지 - 파일 생성, 연결

컨트롤러 연결

HomeController

package com.coding404.myweb.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		return "index";
	}
}
  • RequestMapping으로 url이 http://localhost:8282/myweb/ 이면 index.jsp 연결

NoticeController

package com.coding404.myweb.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
@RequestMapping("/trip")
public class NoticeController {
	
	@Autowired
	@Qualifier("tripService")
	private TripService tripService;
	
	
	//화면 구현 - 컨트롤러 연결
	@RequestMapping("/notice_list")
	public String notice_list() {
		return "trip/notice_list";
	}
	
	//상세화면
	@RequestMapping("/notice_view")
	public String notice_view() {
		return "trip/notice_view";
	}
	
	//글 작성
	@RequestMapping("/notice_write")
	public String notice_write() {
		return "trip/notice_write";
	}
	
	//수정화면
	@RequestMapping("/notice_modify")
	public String notice_modify() {
		return "trip/notice_modify";
	}

}

  • service와 같은 이름으로 멤버변수 생성 후 @Autowired(의존성 주입), @Qualifier (사용할 의존 객체를 선택) 넣기
    controller Qualifier - serviceImpl Service 와 연결 (이름 같게 정의)
  • NoticeController 생성 후 기본 화면 구현
  • @Controller 달아주고, @RequestMapping("/trip") 설정 - trip 폴더에 있는 파일들 (list, view, write, modify)

UserController

package com.coding404.myweb.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/user")
public class UserController {
	
	//이 페이지는 템플릿을 적용하지 않고 사용하려면 타일즈 설정에 직접 추가
	@RequestMapping("/login")
	public String login() {
		return "user/login";
	}
	
	@RequestMapping("/join")
	public String join() {
		return "user/join";
	}
}
  • UserController 생성 후 기본 화면 구현

MyBatis Mapper 파일 위치 분리 기본 생성

root-context.xml

  • 위에서 TripMapper.xml 을 같은 패키지에 담지 않고 분리했기 때문에 그에 맞게 파일 위치 변경
  • setter 주입으로 name="mapperLocations", value="classpath:/위치맞게 변경"
    mapperLocations: 설정 속성
    classpath: 패키지 계층구조의 '최상위 경로', src/main/resources 파일 아래의 파일의 경로를 참조하는 방법

TripMapper 인터페이스

package com.coding404.myweb.trip.service;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TripMapper {

}
  • @Mapper 어노테이션 붙이기
  • 기본적으로 Mapper인터페이스는 Service인터페이스와 같음

TripMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
  <mapper namespace="com.coding404.myweb.trip.service.TripMapper"> <!-- Mapper인터페이스 경로 -->

  </mapper>
  • DOCTYPE과 Mapper 인터페이스 경로 넣기
    <mapper namespace="인터페이스 경로(패키지부터)">

TripVO 생성

package com.coding404.myweb.command;

import java.sql.Timestamp;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TripVO {

	private int tno;
	private String tripdate;
	private String writer;
	private String title;
	private String content;
	private int hit;
	private Timestamp regdate;
	
}
  • DB에서 만든 테이블과 같은 이름으로 멤버변수 생성
  • lombok 이용해서 setter, getter, toString, 기본 생성자, 모든 멤버변수 생성자 넣기

mybatis-confog / mybatis-config.xml (설정은 옵션)

//root-context.xml
<!-- 마이바티스 설정파일 경로 -->
<property name="configLocation" value="classpath:/mybatis-config/mybatis-config.xml" />

//mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
  
  
  <!-- 마이바티스 부연 설정 -->
  <configuration>
  	<typeAliases>
  		<typeAlias type="com.coding404.myweb.command.TripVO" alias="TripVO"/>
  		
  	</typeAliases>
  	
  </configuration>
  • root-context 보면 마이바티스 설정을 해주고
    classpath 아래의 파일(mybatis-config.xml)을 맵핑시켜 마이바티스 설정 추가 가능
  • 마이바티스 typeAlias로 TripVO 넣어주는데 단축명으로 TripVO를 쓰겠다


게시글 등록

notice_writer.jsp 파일 확인

  • input 태그 안의 name (key로 사용) 적기: name은 각각 데이터의 컬럼명으로 설정
<p class="btn_line">
	<!-- 
    <a href="javascript:;" class="btn_baseColor">글작성</a>
    <a href="javascript:;" class="btn_baseColor">목록</a>
 	-->
 	<input type="submit" class="btn_baseColor" value="글작성"/>
 	<input type="submit" class="btn_baseColor" value="목록" onclick=" location.href='notice_list'(컨트롤러이용) "/>
</p>
  • button: 단순한 push 버튼, 연결되는 페이지를 onclick 속성으로 넣어줘야 함
  • 버튼의 a href 태그 ▶ input으로 바꾸고 onclick 넣어서 nortice_list 로 이동

<!-- appForm -->
	<form action="registForm" method="post" class="appForm">
		<fieldset>
			<legend>상담문의 입력 양식</legend>
			<p class="info_pilsoo pilsoo_item">필수입력</p>
			<ul class="app_list">
            	<li class="clear">
                   <label for="email_lbl" class="tit_lbl pilsoo_item">등록일</label>
                   <div class="app_content email_area">
                       <input type="text" id="datepicker_a" name="tripdate" placeholder="날짜를 선택하세요" required="required"></p>
                   </div>
				</li>
						
				<li class="clear">
                   <label for="email_lbl" class="tit_lbl pilsoo_item">작성자</label>
                   <div class="app_content email_area">
						<input type="hidden" />
                        <input type="text" placeholder="작성자" name="writer" required="required"/>
                   </div>
				</li>
				<li class="clear">
					<label for="name_lbl" class="tit_lbl pilsoo_item">제목</label>
					<div class="app_content"><input type="text" class="w100p" id="name_lbl" name="title" placeholder="제목을 입력하세요" required="required"/></div>
				</li>


				<li class="clear">
					<label for="content_lbl" class="tit_lbl">문의내용</label>
					<div class="app_content"><textarea id="content_lbl" name="content" class="w100p" placeholder="간단한 상담 요청 사항을 남겨주시면 보다 상세한 상담이 가능합니다.
전화 상담 희망시 기재 부탁드립니다." ></textarea></div>
				</li>
			</ul>
			<p class="btn_line">
			<!-- 
               <a href="javascript:;" class="btn_baseColor">글작성</a>
               <a href="javascript:;" class="btn_baseColor">목록</a>
 			-->
 				<input type="submit" class="btn_baseColor" value="글작성"/>
 				<input type="submit" class="btn_baseColor" value="목록" onclick=" location.href='notice_list' "/>
            </p>	
		</fieldset>
	</form>
<!-- //appForm -->
  • 사용자가 입력한 데이터를 서버로 전송하기 위한 form태그 사용
  • post 방식으로 action = registForm으로 넘겨서 내용들을 처리 ▶ NoticeController로 넘김
  • 화면에서 적지 않고 넘기면 공백으로 들어가니까 not null 이어도 의미없어짐 - 처리해줘야함
  • notice_write.jsp required="required" 넣어주기 - 여기서는 tripdate, writer, title

NoticeController.jsp 넘어온 내용 처리

[ post 방식 ] 등록, 수정, 삭제

  • 컨트롤러에서 registForm만들기 - 등록하고 리다이렉트로 목록화면으로 넘김
  • TripService 인터페이스에서 public int noticeRegist(TripVO vo);
    insert니까 리턴값은 없고, 0, 1로 성공 실패 확인
    매개변수로 TripVO을 넣음
  • TripServiceImpl에서는 Mapper인터페이스를 부름
  • 그대로 Mapper인터페이스 넣고, Mapper.xml에 insert 쿼리문 넣기

 

	//글 등록
	@RequestMapping(value="/registForm", method=RequestMethod.POST)
	public String registForm(TripVO vo, RedirectAttributes ra) {
		System.out.println(vo.toString());
		
		int result = tripService.noticeRegist(vo);
		String msg = result == 1 ? "문의사항이 정상 등록되었습니다" : "문의 등록에 실패했습니다";
		ra.addFlashAttribute("msg", msg);
		
		return "redirect:/trip/notice_list"; //데이터 받으면 목록화면으로 그냥 넘기기
	}
  • 컨트롤러에서 vo.toString으로 잘 들어오는지 확인
  • result변수로 받고, 3항연산자로 result 값이 1이면 성공, 아니면 실패
  • RedirectAttributes 로 메세지 값 넘기기

notice_list.jsp 성공 실패 확인 

	<!-- 화면에서 msg받아서 alert로 나타냄 -->
	<script>
		var msg = '${msg}'; //글 등록되면 msg변수를 여기로 가져와서 msg 변수에 넣음
		if(msg != '') { //msg가 공백이 아니라면 알림창 띄움
			alert(msg);
		}
	</script>
  • 화면에서 msg 받아서 alert로 나타태기
  • 자바스크립트 코드를 사용하여 msg가 공백이 아니라면 알림창을 띄움
  • 컨트롤러의 registForm에 호출
  • tripserviceimpl의 noticeRegist도 호출
  • 글 등록되면 msg 변수가 목록화면으로 넘어옴

게시글 리스트 - 호출

  • list할 때 format(형식변환) / parse(형변환) - 보통 비즈니스 로직은 서비스 계층에서 이뤄짐
  • cri 제외하고 보기
  • TripVO 객체를 ArrayList에 담아서 list라는 이름으로 가져옴
  • 화면에서는 list를 board라는 이름으로 맵핑 후, 필요한 데이터만 사용하여 반복문으로 처리

게시글 확인

선택한 게시글 확인

  • a링크 클릭 → 컨트롤러로 notice_view 연결
  • 특정 글만 보여야 하기 때문에 @RequestParam으로 tno를 가지고 TripVO 객체 가져오기
	<select id="getContent" resultType="TripVO">
  		select * from trip where tno=#{tno}
  	</select>
  • 하나의 컨텐츠에 대한 select 만들기

  • TripVO를 model에 담아서 뿌림

이전글, 다음글

  • 컨트롤러에서 tno를 가지고, 이전글, 다음글을 나타내는 list를 가져옴 (list 길이 최소 1개, 최대 2개)
<!-- 이전글, 다음글 -->
  	<!-- xml or html에서 부등호는 태그로 인식이 되는데, ADATA는 순수한 문자열의 형태로 인식 시킴 -->
  	<select id="getPrevNext" resultType="TripVO" parameterType="int">
  	<![CDATA[
  		select * from trip
		where tno in ( (select tno from trip where tno < #{tno} order by tno desc limit 1),
					   (select tno from trip where tno > #{tno} limit 1) )
  		order by tno desc
  		]]>
  	</select>
  • 현재 글의 바로 이전 글의 tno를 가져옴: 현재 글보다 작은 tno 값을 가진 이전 글 중에서 가장 큰 값
  • 현재 글의 바로 다음 글의 tno를 가져옴: 현재 글보다 큰 tno 값을 가진 다음글 중에서 가장 작은 값
  • 서브 쿼리 결과를 합쳐서 처리하고, tno 기준으로 내림차순 정렬하면 이전글 - 다음글이 정렬된 상태로 조회
  • <![CDATA[
          쿼리 
       ]]> : 순수한 문자열의 형태로 인식, '<'를 태그로 인식X
  • select * from # where = 스칼라(연산자, 하나의 행과 컬럼), 서브쿼리(EXISTS, IN, ALL, ANY, SOME 여러 행과 컬럼)
  • select * from trip where tno in (서브쿼리, 서브쿼리) 예시로 (1, 3)
<ul class="near_list mt20">
			
				<!-- ${list } 잘 넘어오는지 확인 -->
				<!-- 
					리스트 길이 확인
					1. 글이 2개인 경우 - 이전글 < 현재글인 경우 이전글
					2. 글이 1개인 경우 - 리스트 길이가 1이고, 글 < 현재글인 경우 다음글이 없음
					3. 글이 0개인 경우 - list길이가 없으면 돌지도 않을거임, 따로 안해도 됨
				 -->
				
				<c:forEach var="data" items="${list }"> <!-- list길이가 없으면 돌지도 않을거임, data에는 이전글 다음글이 들어있음 -->
					<c:if test="${fn:length(list) == 1 and data.tno < vo.tno}">
						<li><h4 class="prev">다음글</h4>은 없습니다</li>
					</c:if>
				
					<c:if test="${data.tno > vo.tno}">
						<li><h4 class="prev">다음글</h4><a href="notice_view?tno=${data.tno }">${data.title }</a></li>
					</c:if>
					<c:if test="${data.tno < vo.tno }">
						<li><h4 class="next">이전글</h4><a href="notice_view?tno=${data.tno }">${data.title }</a></li>
					</c:if>
					
					<c:if test="${fn:length(list) == 1 and data.tno > vo.tno}">
						<li><h4 class="next">이전글</h4>은 없습니다</li>
					</c:if>
				</c:forEach>
			
				<!-- 
				<li><h4 class="prev">다음글</h4><a href="javascript:;">추석 연휴 티켓/투어 배송 및 직접 수령 안내</a></li>		
				<li><h4 class="next">이전글</h4><a href="javascript:;">이번 여름 휴가 제주 갈까? 미션 투어 (여행경비 50만원 지원)</a></li>
				 -->
			</ul>
  • ${data.tno < vo.tno}: 현재의 글(vo.tno)보다 data.tno가 작으므로 이전글
  • ${data.tno > vo.tno}: 현재의 글(vo.tno)보다 data.tno가 크므로 다음글

게시글 수정

더보기
<form action="deleteForm" method="post" name="deleteForm">
    <input type="hidden" name="tno" value="${vo.tno}"/>
    ...
    <p class="btn_line txt_right">
        <!-- 수정 버튼 -->
        <a href="javascript:;" class="btn_bbs" onclick="submitForm('notice_modify', 'POST')">글수정</a>
        <!-- 삭제 버튼 -->
        <a href="javascript:;" class="btn_bbs" onclick="submitForm('deleteForm', 'POST')">글삭제</a>
        <a href="notice_list" class="btn_bbs">목록</a>
    </p>
</form>

<!-- JavaScript 함수 추가 -->
<script>
    // JavaScript로 폼을 전송하는 함수
    function submitForm(action, method) {
        // 새로운 폼 엘리먼트 생성
        var form = document.createElement('form');
        form.action = action;
        form.method = method;
        form.style.display = 'none'; // 폼이 화면에 보이지 않도록 함
        
        // 히든 필드 추가
        var tnoField = document.createElement('input');
        tnoField.type = 'hidden';
        tnoField.name = 'tno';
        tnoField.value = '${vo.tno}';
        
        // 폼에 필드 추가
        form.appendChild(tnoField);
        
        // document.body에 폼 추가
        document.body.appendChild(form);
        
        // 폼 전송
        form.submit();
        
        // 사용이 끝난 폼 제거
        document.body.removeChild(form);
    }
</script>
  • 수정화면 클릭시 ?tno={vo.tno}로 단순히 화면 이동 (수정도 post 방식 써야함)
  • placeholder는 힌트, 값X, 값은 value에
  • controller에서 처리하고 modify에서 뿌리기

  • notice_modify.jsp 수정버튼 누를 때 tno같이 넘어가게 해주기, hidden 이용 → 화면 x, 데이터 보내기
  • 수정이나 삭제는 postO, getX(수정은 수정파일로 들어가니까 get해도 무난하지만 post써라, 삭제는 누르면 어디로 가는게 아니라 바로 실행이니까 무조건 post 써야 함)
///////////////////////////////////////참고////////////////////////////////////////
	//수정, 상세 화면이 완전 동일하다면
	//void형은 들어오는 경로가 나가는 경로라서 modify경로로 들어오면 modify로, view로 들어오면 view로 나감
	@RequestMapping({"notice_modify", "notice_view"})
	public void notice_view(@RequestParam("tno") int tno,
							Model model) {
		
		TripVO vo = tripService.getContent(tno);
		model.addAttribute("vo",vo);
	}
  • 수정, 상세 화면이 같을 때 참고 → 버튼 누르면 바로 가는게 아니라 확인하고 submit 보냄

게시글 삭제

  • a 태그로 post 방식으로 보내려면 js 써야함

조회수

  • 클릭할 때 실행 (상세화면)
  • SQL에서 변화하는 값이 X → hit = hit+1

조회수 중복방지 구현

회원가입, 로그인 구현


오늘 하루

더보기

기억에 남는 부분

 

어려운 부분

 

문제 해결 부분

728x90
profile

원지의 개발

@원지다

250x250