[2022/11/02 WED] spring - mymelon 프로젝트

kangsun·2022년 11월 2일
0

Spring

목록 보기
2/24

[01] Spring Boot기반의 mymelon 프로젝트 환경구축


Database

  • JDBC
    sql과 자바를 연동해서 한 곳에서 사용한다.
  • MyBatis Framework
    sql을 따로 관리한다.

1. 프로젝트 생성

  • Spring Starter Project 생성


  • ORM 방식

    테이블 설계에 관련된 데이터베이스 작업을 자바에서 할 수 있다는 것이다. 스프링에서는 JPA방식이 있다.

  • JPA

    외부 응용프로그램을 내부에서 편리하게 사용할 수 있는 라이브러리다.


2. pom.xml 의존성 추가

https://mvnrepository.com/

  • 뷰페이지를 JSP로 지정
  • jstl


  • fileupload


  • 자바 웹메일 라이브러리
  • json-simple 라이브러리
  • 여기로 다운로드 받아진다.
  • 업데이트 수시로 해주기
-----------------------------------------------------------------------------
    <dependencies> <!-- 자바 외부 라이브러리 (.jar) 지정 -->

        <!-- 사용자 추가 라이브러리(의존성) 시작 -->

        <!-- 자바 메일 라이브러리 -->
        <!-- https://mvnrepository.com에서 mail 검색 -->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>

        <!-- json-simple 라이브러리 -->
        <!-- https://mvnrepository.com에서 json검색 -->
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1.1</version>
        </dependency>

        <!-- 파일업로드/다운로드 관련 라이브러리 -->
        <!-- https://mvnrepository.com에서 fileupload검색 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>

        <!-- 뷰페이지 관련 라이브러리(JSP, JSTL) -->
        <!-- https://mvnrepository.com에서 jasper검색 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>9.0.52</version>
        </dependency>

        <!-- https://mvnrepository.com에서 jstl 검색 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- 사용자 추가 라이브러리 끝 -->

    </dependencies><!-- 라이브러리 지정 끝 -->
-----------------------------------------------------------------------------

3. application.properties 환경설정 파일

  • 한글 인코딩 해주기
  • 서버 연결, 뷰페이지 경로 설정
#주석 샵
#/src/main/resources/application.properties 환경설정 파일

#Spring BootWAS(Tomcat) 내장되어 있음 (기본 port번호 8080)
#톰캣서버의 http port번호 변경
server.port=9095

#주의사항 JSP, Thymeleaf, Mustache는 공동으로 사용할 수 없음
#JSP를 뷰페이지로 사용할 경우 pom.xml에 라이브러리 추가해야함
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
  • JSP페이지가 수정이 되면 자동으로 서버 재시작 설정 추가
  • 파일 업로드 용량제한 설정
  • 오라클 DB 환경설정
------------------------------------------------------/src/main/resources/application.properties
#application.properties 환경 설정 파일

#주석 샵
#/src/main/resources/application.properties 환경설정 파일

#Spring BootWAS(Tomcat) 내장되어 있음 (기본 port번호 8080)
#톰캣서버의 http port번호 변경
server.port=9095

#주의사항 JSP, Thymeleaf, Mustache는 공동으로 사용할 수 없음
#JSP를 뷰페이지로 사용할 경우 pom.xml에 라이브러리 추가해야함
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

#JSP페이지가 수정이 되면 자동으로 서버 재시작
server.servlet.jsp.init-parameters.development=true

#파일 업로드 용량제한 설정
spring.servlet.multipart.max-file-size=500MB
spring.servlet.multipart.max-request-size=500MB

#오라클 DB 환경설정 
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
spring.datasource.username=system
spring.datasource.password=1234
-------------------------------------------------------------------

  • 서버 시작하는 java파일 run 시켜주기



  • prefix 경로 똑같이 만들어주기
  • resource 폴더에 css/images/js 폴더 만들고 파일 업로드하기

    style.css는 강사님이 주신 파일로 다운받기



[02] mymelon 프로젝트 - mediagroup 테이블

빨간색 부모테이블
초록색 자식테이블
두 페이지를 관리해야한다.

부모의 글번호를 가지고 다녀야한다. (ex.39번)
자식페이지에도 그 글번호를 가져와야한다.

강남스타일을 클릭하면 mp3듣기 view페이지가 나오면 된다.



JdbcTemplate
데이터베이스를 클래스로 만들어놓은 템플릿을 사용하면 된다.
(DBOpen같은 게 여기 안에 다 담겨져있다)
RowMapper
jsp에서 if(rs.next)로 작업했던 것이 RowMapper 클래스 안에 담겨져있다.


작업폴더 생성



1. mediagroup 테이블 생성

부모테이블(미디어 그룹) 먼저 생성

-------------------------------------------------/src/main/webapp/WEB-INF/sql/mediagroup_sql.txt

-- 미디어 그룹 테이블 생성
CREATE TABLE mediagroup (
  mediagroupno NUMBER        NOT NULL PRIMARY KEY, -- 그룹번호
  title        VARCHAR2(255) NOT NULL              -- 그룹제목 
);

-- 시퀀스 생성
create sequence mediagroup_seq;

mediagroupno  title
------------  ---------------
1             2021년 댄스음악
2             2020년 K-POP
3             2019년 발라드
  • txt 파일로 테이블 작성해보기
  • cmd로 테이블 생성, 시퀀스 생성, 커밋까지 완료하기

2. Backend단


1) DTO 클래스

변수, 기본생성자, toString 생성

package kr.co.itwill.mediagroup;

public class MediagroupDTO {

	private int mediagroupno;
	private String title;
	
	//기본생성자
	public MediagroupDTO() {}

	//getter, setter
	public int getMediagroupno() {
		return mediagroupno;
	}

	public void setMediagroupno(int mediagroupno) {
		this.mediagroupno = mediagroupno;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	@Override
	public String toString() {
		return "MediagroupDTO [mediagroupno=" + mediagroupno + ", title=" + title + "]";
	}
	
}//class end

2) 컨트롤러

  • controller 객체 생성 확인

  • HomeController 생성
    인트로페이지 쉽게 접근하기 위함.

  • HomeController - @RequestMapping

  1. @RequestMapping으로 실행하고자하는 명령어(경로명)을 넣어주고
  2. 그 명령어가 들어왔을 때 실행해줄 ModelAndView home()메서드 만들기
  3. ModelAndView - 뷰페이지로 이동해줄 명령어 실행
  4. ModelAndView 객체 생성
  5. setViewName() - 어디 뷰페이지를 보여줄 것인지?
  6. redirect - 내가 등록한 명령어를 호출해준다
  7. /mediagroup/list.do 미디어그룹 폴더안에 있는 list.do 호풀한다는 것/

  • MediagroupCont - @RequestMapping
  1. "mediagroup/create.do" URL로 이페이지에 들어오게 되면
  2. createForm이 실행된다
  3. "mediagroup/createForm" mediagroup폴더에 있는 createForm 페이지가 실행되는 것이다.

createForm.jsp view단 생성 후 확인


MediagroupCont.java 의 실행 흐름


mediagroup/create.do가 url로 들어오면
mediagroup/createForm페이지가 반환된다.(return)


createProc() @RequestMapping

createFrom을 만들었으므로, post방식을 @RequestMapping한다.

먼저 실행 결과를 보여줄 코드를 작성한다.
dao는 작성하지 않았으므로 아직 오류가 난다.
if문으로 결과실패 페이지, 결과 성공페이지를 연결시켜준다.

createProc() @RequestMapping 추가

1) 실행실패 view페이지 (jsp) 만들기

실패 페이지는 이 페이지 하나로 통일시킨다.



※ 다양한 어노테이션

@Controller : 접속자가 브러우저에서 URL을 입력하여 접근시 주소에 해당하는 메소드를 구현하고, 데이터를 처리하여, JSP 페이지로 이동하는 기능을 함.
① 주소에 해당하는 메소드 실행
② DTO, DAO등의 데이터 처리
③ JSP 페이지로 이동

@Component : Spring이 필요시 자동으로 객체를 생성함.
Spring 컨테이너가 객체를 만들도록 권한을 부여
범용적인 Bean

@Autowired : Spring이 필요시 자동으로 객체를 생성하여 필드(Instance variable, 객체 변수)에 할당함.
@Component 선언에 따라 자동으로 만들어진 객체를 연결하는 어노테이션

@Qualifier("타입이 맞은 빈 객체 이름") : 지시자를 통해 빈 객체 지명 가능
같은 이름의 클래스가 존재하면 생성되는 클래스에 이름을 부여하고 구분해서 객체를 할당 받음

@Repository : DAO를 스프링에 인식시키기 위해서 주로 사용
모델클래스로 지정하면 스프링컨테이너에서 관리를 해줌
여기 경로 안에 @Repository를 달고 있으면 인식을 해주고 그것을 객체로 만들어 준다
DAO관련 빈을 자동 등록 대상으로 만들때 사용한다
@Component를 부여해도 되지만, 스프링에서는 @Repository를 권장한다
@Component와 차이점은 Exception이 발생했을 때, DB Exception을 DataAccessException을 변환한다
같은 클래스가 다른 패키지에 존재하면 이름을 할당하여 구분해야 한다

@ResponseBody : jsp view를 이용하여 출력하지 않고 response 객체에 직접 출력한다
JSON형식의 출력값

@RequestBody : JSON형식의 입력변수

@Inject : 사용자가 직접 만들지 않고 스프링에서 생성해서 주입을 시킴

@RestController : 컨트롤러(json을 리턴할 경우)

@Service : 로직처리

@RequestMapping : URL mapping

@RequestParam : get/post방식으로 전달되는 매개변수(개별변수)

@ModelAttribute : get/post방식으로 전달되는 매개변수(클래스타입)

@PathVariable : URL에 포함된 변수


3) DAO 클래스

  • 원래 myweb에서 DAO작업 했던 것.


    객체생성은 ssi.jsp에서 했다.

  • spring에서는 위에 있는 작업을 간단히 해주는 어노테이션이 있다.
    @Service

    컨트롤러처럼 객체가 생성되었다.



    객체까지 만들어서 모아준다.
    자식에 해당하는 Repasitory라는 어노테이션도 들어갈 수 있다.(서비스보단 협소한 개념)
    위에있는 작업을 따로 하지 않아도 된다는 의미.
    객체를 미리 만들어두고 기다리겠다는 의미.

→ spring이 분산화에 최적화된 프로그램
웹, 뷰, 데이터는 왔다갔다 하기 힘들다. spring은 집중적으로 사용해야되는 것들을 (서버연결) 같은 걸 미리 만들어준 것이다.

@Repository service보다 조금더 구체화시킨 어노테이션이다.



  • JdbcTemplate클래스


    JdbcTemplate클래스는 이걸 가져와준다.



    이렇게 끌어온다.



    이미만들어진 객체(@Repository)를 끌어다 쓸때 new를 쓰지 않는다. new 대신 @Autowired를 써준다.



  • @Autowired
    이미 만들어진 객체 안에 또 다른 객체를 넣을 때 사용.
    자동으로 만들어진 객체를 연결하는 어노테이션

    원래 myweb jsp에서 DAO를 가져오려면 객체를 new로 생성해줘야 했다.

    하지만 Spring에선 @Controller로 이미 객체가 생성되어져있는 클래스 안에 또 다른 객체를 불러오기 위해선 @Autowired를 붙여줘야한다.
    이미 객체가 만들어져 있는데 new를 사용하는 것은 오류가 난다.
    자동으로 만들어진 객체라는 의미로 이걸 붙여주면 알아서 프로그램이 객체로 인식해준다.

정리


이렇게 이미 만들어진 객체에 객체를 끌어다 쓰기위해선 @Autowired 사용해준다.
이 모든걸 spring container가 관리를 해준다.


비지니스 로직 구현

  • 게시물 글쓰기


    JdbcTemplate 클래스 안에 update 라는 함수가 있다.
    sql문을 toString으로 넣어주는 것.

  • cmd 행 확인


    등록을 누르면 아직 오류가 난다.

  • 게시물 목록



    RowMapper : 10개 행을 가져오면 RowMapper가 10개를 딱 잡아준다. 그리고 자동으로 커서가 내가 select한 내용을 잡아주는 것.

    같은 이름으로 조회되는데, jdbc가 들어간것으로 선택.


    자동으로 생성된다.
    rowNum - 행번호도 가지고있다.
	public List<MediagroupDTO> list() {
		List<MediagroupDTO> list=null;
		try {
			sql=new StringBuilder();
			sql.append(" SELECT mediagroupno, title ");
			sql.append(" FROM mediagroup ");
			sql.append(" ORDER BY mediagroupno DESC ");
			
			RowMapper<MediagroupDTO> rowMapper=new RowMapper<MediagroupDTO>() {
				@Override
				public MediagroupDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
					
					MediagroupDTO dto=new MediagroupDTO();
					dto.setMediagroupno(rs.getInt("mediagroupno"));
					dto.setTitle(rs.getString("title"));
					
					return dto;
				}//mapRow() end
			};//rowMapper end
			
			list=jt.query(sql.toString(), rowMapper);
		
		} catch (Exception e) {
			System.out.println("미디어그룹등록실패:" + e);
		}//end
		return list;
	}//list() end
	

  • DAO 글개수 sql 작성하기

MediagroupCont.java 컨트롤러

  • 게시물 목록과 글개수 view페이지에 옮겨줄 코드 작성
    DAO에서 sql로 작성해서 올려준 데이터베이스를 MediagroupCont에 view단으로 연결해주는 코드를 작성해준다.

    list 와 count 변수를 자식페이지에서 사용할 수 있게된다.

Front단 - 목록페이지 list.jsp


보여지는 페이지를 만들어야 한다.

  • 데이터가 없을 때 (false)
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
...
	<c:if test="${requestScope.count==0}">
		<table><tr><td>게시판에 글 없음!!</td></tr></table>
	</c:if>


c:if 부분은 count함수가 0이면 sql문 데이터가 없다는 의미로, 데이터를 가져오지 못했을 때 보여지는 페이지이다.

  • 데이터가 있으면 (true)

    <c:forEach var="dto" items="${list}">
    list를 한 줄씩 뽑아오라는 의미이다.

    myweb에선 이렇게 표현되어있다.

list 게시물 목록 페이지의 실행 흐름

정리

  1. DAO에서 데이터베이스를 연결해 데이터를 가져온다.
  2. Controller 페이지에서 데이터베이스로 연결한 데이터를 가지고 그 데이터와 view단을 연결시켜주고, view단에 올려줄 값들을 변수에 담아준다.
    (-어떤 URL요청 값을 가져올 것인지,
    -그 요청값에 어떤 페이지를 열 것인지,
    -컨트롤러와 view페이지를 연결시켜주고,
    -dao에서 가져온 데이터를 연결시켜주고,
    -view페이지에 보내줄 변수를 담아준다.)
  3. view단에는 ${ }에 값만 입력해주면 된다.

페이징





Controller에서 가져온 startPage값을 value에 담아주고, 그 값을 또 변수인 var에 담아준 것이다.

  • DAO
	//페이징 리스트
	public List<MediagroupDTO> list2(int start, int end) {
		List<MediagroupDTO> list=null;
		try {
			sql=new StringBuilder();
			sql.append(" SELECT AA.* ");
			sql.append(" FROM ( ");
			sql.append("		SELECT ROWNUM as RNUM, BB.* ");
			sql.append("		FROM ( ");
			sql.append("				SELECT mediagroupno, title ");
			sql.append("				FROM mediagroup ");
			sql.append("				ORDER BY mediagroupno DESC ");
			sql.append("			  )BB ");
			sql.append("	   )AA ");
			sql.append(" WHERE AA.RNUM >=" + start + " AND AA.RNUM<=" + end);
			
			RowMapper<MediagroupDTO> rowMapper=new RowMapper<MediagroupDTO>() {
				@Override
				public MediagroupDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
					MediagroupDTO dto=new MediagroupDTO();
					dto.setMediagroupno(rs.getInt("mediagroupno"));
					dto.setTitle(rs.getString("title"));
					return dto;
				}//mapRow() end
			};//rowMapper end
			
			list=jt.query(sql.toString(), rowMapper);
			
		} catch (Exception e) {
			System.out.println("미디어그룹페이징실패:" + e);
		}//end
		return list;
	}//list2() end
  • MediagroupCont
	@RequestMapping("mediagroup/list.do")
	public ModelAndView list(HttpServletRequest req) {
		ModelAndView mav=new ModelAndView();
		mav.setViewName("mediagroup/list");
		
		int totalRowCount=dao.totalRowCount(); //총 글 개수
		
		//페이징
		int numPerPage   = 5; //한 페이지당 레코드 개수
		int pagePerBlock = 10; //페이지 리스트
		
		String pageNum=req.getParameter("pageNum");		//현재페이지 가져오기
		if(pageNum==null) {
			pageNum="1";
		}//if end
		
		int currentPage=Integer.parseInt(pageNum);		//현재페이지: currentPage변수에 넣는다
		int starRow    =(currentPage-1)*numPerPage+1;	//시작페이지: (현재페이지-1) * 5+1 → 1페이지면 0 / 2페이지면 6
		int endRow	   =currentPage*numPerPage;			//마지막페이지:현재페이지*5 →         1페이지면 5 / 2페이지면 10 
		
		
		//페이지 수
		double totcnt =(double)totalRowCount/numPerPage;//페이지수: 총 글 개수 / 5 → 만약 12개? 2.4
		int totalPage = (int)Math.ceil(totcnt);			//페이지수반올림: 2.4 → 3페이지
		
		double d_page =(double)currentPage/pagePerBlock;//현재페이지/페이지리스트: 2/10 → 0.2
		int Pages	  =(int)Math.ceil(d_page)-1;		//0.2 반올림 1-1 → 0
		int startPage =Pages*pagePerBlock;				//0*10
		int endPage	  =startPage+pagePerBlock+1;		
		
		
		List list=null;
		if(totalRowCount>0) {
			list=dao.list2(starRow, endRow);
		}else {
			list=Collections.EMPTY_LIST;
		}//if end
		
		//int number=0;
		//number=totalRowCount-(currentPage-1)*numPerPage;
		
		//mav.addObject("number", number);
		mav.addObject("pageNum", currentPage);
		//.addObject("starRow", starRow);
		//mav.addObject("endRow", endRow);
		mav.addObject("count", totalRowCount);
		//mav.addObject("pageSize", pagePerBlock);
		mav.addObject("totalPage", totalPage);
		mav.addObject("startPage", startPage);
		mav.addObject("endPage", endPage);
		mav.addObject("list", list);
		return mav;
	}// list() end
  • list.jsp
	<!-- 페이지 리스트 -->
	<c:if test="${requestScope.count>0}">
		<c:set var="pageCount" value="${requestScope.totalPage}"/>
		<c:set var="startPage" value="${requestScope.startPage}"/>
		<c:set var="endPage" value="${requestScope.endPage}"/>
		
		<div class="content">
		<c:if test="${endPage>pageCount}">
			<c:set var="endPage" value="${pageCount+1}"/>
		</c:if>
		
		<c:if test="${startPage>0}">
			<a href="./list.do?pageNum=${startPage}"><</a>
		</c:if>
		
		<c:forEach var="i" begin="${startPage+1}" end="${endPage-1}">
			<c:choose>
				<c:when test="${pageNum==i}"><span style="font-weight: bold">${i}</span></c:when>
				<c:when test="${pageNum!=i}"><a href="./list.do?pageNum=${i}">[${i}]</a></c:when>
			</c:choose>
		</c:forEach>
		
		<c:if test="${endPage>pageCount}">
			<a href="./list.do?pageNum=${startPage+11}">></a>
		</c:if>
		</div>
	</c:if>
// 현재 페이지 번호 (정수형)
        int currentPage = Integer.parseInt(pageNum);
        // 페이지의 첫번째 글 번호 ((현재 페이지-1)*한페이지당 글수+1)
        int startRow = (currentPage - 1) * numPerPage + 1;
        // 페이지의 마지막 글 번호 (현재 페이지*한페이지당 글 수)
        int endRow = currentPage * numPerPage;
        // 현재 2페이지라면 시작 번호는 6, 끝 번호는 10
        
        // 총 페이지 수 (총 글 갯수 / 한페이지당 글 수)
        double totcnt = (double) totalRowCount / numPerPage;
        int totalPage = (int) Math.ceil(totcnt);

        // (현재 페이지 번호

/ 페이징 목록의 페이지 수)
        double d_page = (double) currentPage / pagePerBlock;
        // 묶음 페이지 번호 : 페이징 목록의 페이지 수를 한번에 묶음 (1~10은 0, 11~20은 1)
        int Pages = (int)Math.ceil(d_page)-1;
        // 시작 페이지 번호 (묶음 페이지 번호 * 블럭당 페이지 수)
        int startPage = Pages * pagePerBlock+1;
        // 끝 페이지 번호 (시작 페이지 번호 + 블럭당 페이지 수)
        int endPage = startPage + pagePerBlock-1;
profile
코딩 공부 💻

0개의 댓글