👨🏫컨트롤러 서비스 저장소 왔다갔다 하는거 연습해야 한다
화면에 어떤 정보 보낼지 고민,
상황별로(암호변경시) 어떤정보가 필요한지 어떤 정보들을 보낼지 고민!
많은 연습을 해야한다 연습만이 살길…
세션
: 기록하고 싶은 일들을 세션에 보관한다
과거에 했던 행적을 기록해서 유지하며 세션이 유지되는 시간 기본값은 30분이다
redis 사용하면 좋은데 지금은 파일기반 mongoDB로 실습진행!
- 로그인 후 30분이 지나면 그 세션값은 자동적으로 소멸된다
- 세션 기록정보가 있으면 ➡️ 로그인 한 사람이구나!
- 기록정보가 없으면 ➡️ 로그인 하지 않았구나!
메모리
기반의 세션,DB(파일)
기반의 세션,DB메모리
기반 세션
메모리
기반 세션 : 눈으로 데이터 확인이 어렵다
컴퓨터 켜져 있는 동안만 메모리 저장 ➡️ 속도 측면에서는 메모리 기반 세션이 빠르다DB(파일)
기반 세션 : 파일로 저장되어 메모리가 눈으로 확인 가능하다
ex) mongodbIn-Memory DB
: 디스크(Disk) 대신 메모리(Memory)를 사용
전원 꺼지면 메모리 사용 불가
ex) redis, memchaed
🌎참고 RDBMS vs NoSQL vs InMemory
세션용 라이브러리 추가
<!-- mongodb session -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-mongodb</artifactId>
</dependency>
httpSession.setAttribute("키", "실제값");
- MemberController.java 에서 로그인 성공 후 세션에 적절한 값을 추가한다
mService loginMember ➡️ 반환값으로member
를 받는다반환값으로
member
를 받는 이유는 return값을member
로 줘야session
에id
를 저장 가능하기 때문이다!
memberservice에서 쿼리 작성하여 로그인 해도 되지만 어떤 방법을 사용하던 로그인 후 return값을int
로 주는 경우는session
에 정보를 저장할 수 없다(누구인지 정보가 없는데 저장할 수는 없기 떄문)
@Autowired
HttpSession httpSession;
...
// 로그인 화면 이동(GET)
// 127.0.0.1:8080/ROOT/join.do
@GetMapping(value = "/login.do")
public String loginGET() {
return "login";
}
// 로그인 버튼(POST)
@PostMapping(value = "/login.do")
public String loginPOST(@ModelAttribute Member member) {
// 1. 전송되는 값 확인
System.out.println("전송값" + member.toString());
// 2. service 호출해서 전송
Member retmMember = mService.loginMember(member);
// 3. 결과값 확인 후 처리
// 4. 적절한 페이지로 이동(GET으로 이동)
if (retmMember != null) { // 로그인 된 경우
System.out.println("로그인값" + retmMember.toString());
// 세션에 적절한 값을 추가함. 유지 시간 기본값(30분동안) 유지
httpSession.setAttribute("SESSION_ID", retmMember.getId());
httpSession.setAttribute("SESSION_NAME", retmMember.getName());
return "redirect:/home.do";
} // 로그인 되지 않은경우
return "redirect:/member/login.do";
}
암호가 있으니
get
으로 보내면 안된다!POST
로 보내기
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로그인</title>
</head>
<body>
<div style="border: 1px double #0000006e; padding: 20px;">
<h3>로그인</h3>
<form th:action="@{/member/login.do}" method="post">
<label style="display: inline-block; width: 100px;">아이디</label><input type="text" name="id" /><br />
<label style="display: inline-block; width: 100px;">암호</label><input type="password" name="password" /><br />
</hr>
<input type="submit" value="로그인" />
</form>
</div>
</body>
</html>
memberservice.java에서 쿼리 작성하여 로그인 해도 된다
하지만 return값은 반드시member
로 와야session
에 id를 저장가능하다
// 로그인
public Member loginMember( Member member ){
try {
return mRepository.selectMemberLogin(member.getId(), member.getPassword());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
MongoRepository
<Member, String>
➡️ <엔티티, 엔티티id타입>
value
= 조건 값_id
,password
fields
= (Member에서)가져올 내용_id
,name
value
안에,
로 연결된 값들은and
조건이다
...
@Repository
public interface MemberRepository extends MongoRepository<Member, String>{
...
// 로그인용
@Query(value = "{_id : ?0, password : ?1}", fields = "{_id : 1, name : 1}")
public Member selectMemberLogin(String id, String pw);
- 홈 화면은 세션이 필요하다 ➡️ 사용자의 로그인 여부를 파악하여 홈화면 다르게 구현하기 위해
- HomeController.java에서도 세션 사용하여 로그인 여부를 파악 한 후 return한다
- MemberController.java 에서 로그인시 키값을 두개 넣었다!
SESSTION_ID
,SESSTION_NAME
- HttpSession.getAttribute(String arg0) : Object ➡️ arg0은 string 타입이다
@Autowired
HttpSession httpSession;
...
// return 될 파일명은 string 이니까 string 사용
public String homeGET() {
String userid = (String) httpSession.getAttribute("SESSION_ID");
if(userid == null){
System.out.println("로그인 안됨");
} else {
System.out.println("로그인 됨");
}
return "home"; //=> resource/templates/home.html
}
- 홈화면 구성시 로그인(세션)기록 여부에 따라 페이지를 다르게 구현한다
- 로그인/회원가입 ⇒ 로그인 안된경우 출력
- 로그아웃/마이페이지 ⇒ 로그인 된 경우 출력
th:if
사용하여 로그인 여부에 따라 화면을 출력한다
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
...
<body>
<h3>홈화면</h3>
// 로그인 안된 경우
<th:block th:if="${sessing.SESSION_ID == null}">
<a th:href="@{/member/login.do}">로그인</a>
<a th:href="@{/member/join.do}">회원가입</a>
</th:block>
// 로그인 된 경우
<th:block th:if="${sessing.SESSION_ID != null}">
<a th:href="@{/member/logout.do}">로그아웃</a>
<a th:href="@{/member/mypage.do}">마이페이지</a>
</th:block>
<a th:href="@{/board/boardlist.do}">게시판</a>
<a th:href="@{/item/seller.do}">판매자</a>
</body>
...
로그아웃
get
으로 보내면 안된다!post
사용
세션 메모리기반 ⇒ 데이터 확인이 어렵다
- 로그아웃시 기존의 세션 내용 지우기
➡️ 세션의 내용을 (비우는)null
이 아니라 세션을 새로 시작하는게 맞는 방법이다- 세션 초기화 =
httpSession.invalidate();
// 로그아웃
@PostMapping(value = "/logout.do")
public String logoutPost() {
httpSession.invalidate(); // 세션 초기화
return "redirect:/home.do";
}
@Autowired
➡️bean
에 의해 객체 생성이 되어있어 사용가능@Bean
➡️ 서버 구동시 자동으로 객체를 생성하는 것!
보기 편하지만 나중에 한계가 있다@Bean
붙어있는경우 객체가 생성되어 있구나 하고 이해하기
jackson
은 내용이 보여서 편리하지만 원래 session은 내용을 볼 용도로 사용하는게 아니다!
jackson
은entity
가 안들어가고jdk
는entity
가 통으로 들어간다jdk
에서entity
를 통으로 꺼내어 필요한 정보만 꺼내 쓴다
MongoDB
세션저장시
컬렉션명, 유지 시간 = ( )
+클래스명 obj = new 생성자(유지 시간)
일반웹인 경우 대부분jdk
사용하며, json문서인 경우Jackson
방식 사용
byte
이기 때문에 확인 불가
collectionName
= DB에 생성될 컬렉션 이름- EnableMongoHttpSession.maxInactiveIntervalInSeconds : int
= 시간 (600이 10분)
@EnableMongoHttpSession(collectionName = "boot_sessions", maxInactiveIntervalInSeconds = 60*30 )
...
@Bean
public JdkMongoSessionConverter jdkMongoSessionConverter() {
return new jdkMongoSessionConverter(Duration.ofMinutes(30))
}
DB에서
attr
이 데이터 부분이다
entity
가attr byte[]
형태로 온다
로그인 한 사용자는 로그인 정보가 추가로 저장되었기 때문에 데이터가 더 많은것을 확인 할 수 있다.
json문서인 경우 이 방식 사용! json 형태로 저장되며 내용도 확인이 가능
하지만 json문서에서는token
방식을 더 많이 사용한다
// jackson방식
@Bean
public JacksonMongoSessionConverter jacksonMongoSessionConverter() {
return new JacksonMongoSessionConverter();
}
@Autowired
사용시@bean
으로 객체가 생성되어 있음을 알고있어야 한다
메모리 ➡️@Bean
mongodb jdk✔️ 조회수 1증가 과정(BoardController.java)
1. 글 목록 조회에서 세션에 BOARD_HIT값을true
로 설정
httpSession.setAttribute("BOARD_HIT", true);
2. 상세페이지 1개 조회에서 httpSession의BOARD_HIT
값을 가져온 후
3.if(isHit == true)
이면
조회수 1 증가bService.boardUpdateHit(no);
+ BOARD_HIT값을false
로 변경httpSession.setAttribute("BOARD_HIT", false);
➡️ 이후 BOARD_HIT값을true
로 변경하려면 글목록 페이지로 이동해야
글 목록 페이지 로딩시 세션의 BOARD_HIT값이 다시true
로 설정된다.
...
@Autowired
HttpSession httpSession;
...
// 상세페이지 1개 조회
@GetMapping(value = "/boardone.do")
public String boardOneGET(Model model,
@RequestParam(name = "no", defaultValue = "0") long no) {
if (no == 0) { // 번호가 없는경우, 오류처리
return "redirect:/board/boardlist.do";
}
// 목록에서 클릭시 조회수 1증가 (최초 1회)
boolean isHit = (boolean) httpSession.getAttribute("BOARD_HIT");
// 0. 조회수 증가 (조회수가 증가된 게시물을 가져와야하니 맨 위에 위치)
// 세션으로 처리
if(isHit == true){
bService.boardUpdateHit(no);
// 목록으로 가야 true로 바뀐다
httpSession.setAttribute("BOARD_HIT", false);
...
//글 목록조회
@GetMapping(value = "/boardlist.do")
public String boardListGET(Model model,
@RequestParam(name = "txt", defaultValue = "", required = false) String txt,
@RequestParam(name = "page", defaultValue = "1", required = false) int page) {
// 목록에서 세션에 BOARD_HIT값을 true로 설정
httpSession.setAttribute("BOARD_HIT", true);
...
- 상세페이지 1개 조회 메서드에서 조회시
"BOARD_HIT" = false
설정시점에"BOARD_NO" = no
로 세팅한다.- BOARD_NO를 변수
boardNo
로 설정 후
long boardNo = (long) httpSession.getAttribute("BOARD_NO");- 이전글/다음글로 이동하여
if(no != boardNo)
조건을 만족하는 경우- 조회수를 1 증가
bService.boardUpdateHit(no);
+ BOARD_NO를 이동된 글의 no로 재설정한다httpSession.setAttribute("BOARD_NO", no);
// 이전글 다음글로 이동시 조회수 증가
long boardNo = (long) httpSession.getAttribute("BOARD_NO");
if(no != boardNo){
bService.boardUpdateHit(no);
httpSession.setAttribute("BOARD_NO", no);
}
- 세션에서 SESSION_ID를 가져온 후 로그인 된 사용자인 경우만 접근 가능하게 조건을 설정해 준다
➡️ 나중에 시큐리티 사용하면 세션관련 코드 작성 안해도 됨 지금은 원리를 이해하는 과정- 로그인 된 사용자가 마이페이지로 이동시 처음 뜨는 화면은 마이페이지 접속시 주소 menu뒤에 아무번호도 붙지 않는다면 1로 설정한다
- menu가 1인경우
// 마이페이지 생성
// http://127.0.0.1:8080/ROOT/member/mypage.do
@GetMapping(value = "/mypage.do")
public String mypageGET(){
// 세션에서 정보 가져오기
String userid = (String) httpSession.getAttribute("SESSION_ID");
// 로그인 되지 않았다면 로그인 페이지로 이동
if(userid == null){
return "redirect:/member/login.do";
}
if(menu == 0){
//처음 마이페이지 접속시 주소 menu뒤에 아무번호도 붙지 않으면 1로 설정
return "redirect:/member/mypage.do?menu=1";
}
return "mypage";
}
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>마이페이지</title>
</head>
<body>
<div style="border: 1px double #0000006e; padding: 20px;">
<h3>마이페이지</h3>
<hr />
<a th:href="@{/member/mypage.do?(menu=1)}"><button>정보변경</button></a>
<a th:href="@{/member/mypage.do?(menu=2)}"><button>암호변경</button></a>
<a th:href="@{/member/mypage.do?(menu=3)}"><button>회원탈퇴</button></a>
<hr />
<div th:if="${#strings.toString(param.menu) == '1'}">
정보변경
<hr />
</div>
<div th:if="${#strings.toString(param.menu) == '2'}">
암호변경
</div>
<div th:if="${#strings.toString(param.menu) == '3'}">
탈퇴하기
</div>
</div>
</body>
</html>
<body>
에 footer 추가
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<footer th:fragment="footerFragment">
<p>copyright 2022</p>
</footer>
</html>
<body>
하단에 footer추가
<div th:replace="~{파일명 :: fragment이름}"></div>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
...
<div th:replace="~{footer :: footerFragment}"></div>
...
암호변경을 위해 entity에 암호변경용 임시변수 생성
@Transient
➡️ 임시변수 생성시 사용, DB에는 들어가지 않는 항목이다
@Transient
private String password1 = null; //암호변경 임시변수
- 화면이 로딩 되었을때 정보가 입력되어 있어야 한다
- 3개 각각의 메서드를 생성해도 되지만 if문으로 한번에 처리
MemberController.java에서if(menu=1)..else(menu=2)..else(menu=3)
을 사용하기 위해 메뉴별로 반복문을 실행한다<div th:if="${#strings.toString(param.menu) == '1'}">
를 사용하여<form th:action="@{/member/mypage.do(menu=1)}" method="post">
MemberService
회원정보 조회시 return된obj
는 1개이니 반복문 돌릴 필요 없음th:value="${obj.name}"
위와같은 형태로 담아 바로 출력한다
- 보여지는 화면만 다를 뿐 회원정보 수정시
MemberController
내부의 같은 메서드로 보낸다
action 부분의파라미터
만 다를 뿐 멤버 컨트롤러의if문
에 따라 다르게 처리된다
컨트롤러를 3개 만들기 싫어서@RequestParam
으로menu
받고 컨트롤러 안의 같은 메서드로 넣고if문
으로 3개 동시에 처리
😧❓ mypage.html에서 메뉴별 페이지 생성시 주소설정
- 웹보드 :
<form th:action="@{/member/mypage.do(menu=1)}”
(menu=1)
⇒ 컨트롤러에서 메뉴별 if사용하려고 한것- 나 :
<form th:action="@{/member/mypage.do?menu=1}”
menu=1
⇒ 이렇게 작성하면 컨트롤러에서 메뉴 1 post 따로 2 post따로 만들어야 된다고 하지만 작동은 된다..
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>마이페이지</title>
</head>
<body>
<div style="border: 1px double #0000006e; padding: 20px;">
<h3>마이페이지</h3>
<div th:text="| 아이디 : ${session.SESSION_ID} |"></div>
<div th:text="| 이름 : ${session.SESSION_NAME} 님 로그인중 |"></div>
<hr />
<a th:href="@{/member/mypage.do?(menu=1)}"><button>정보변경</button></a>
<a th:href="@{/member/mypage.do?(menu=2)}"><button>암호변경</button></a>
<a th:href="@{/member/mypage.do?(menu=3)}"><button>회원탈퇴</button></a>
<hr />
<div th:if="${#strings.toString(param.menu) == '1'}">
정보변경
<hr />
<form th:action="@{/member/mypage.do(menu=1)}" method="post">
<label style="display: inline-block; width: 100px;">이름</label><input type="text" name="name" th:value="${obj.name}" /><br />
<label style="display: inline-block; width: 100px;">연락처</label><input type="text" name="phone" th:value="${obj.phone}" /><br />
<hr />
<input type="submit" value="변경하기" />
</div>
<div th:if="${#strings.toString(param.menu) == '2'}">
<form th:action="@{/member/mypage.do?(menu=2)}" method="post">
<label style="display: inline-block; width: 100px;">현재암호</label><input type="password" name="password" /><br />
<label style="display: inline-block; width: 100px;">변경암호</label><input type="password" name="password1" /><br />
<!-- 아래는 유효성검사용 -->
<label style="display: inline-block; width: 100px;">변경암호확인</label><input type="password" /><br />
<hr />
<input type="submit" value="암호변경하기" />
</form>
</div>
<div th:if="${#strings.toString(param.menu) == '3'}">
<form th:action="@{/member/mypage.do?(menu=3)}" method="post">
탈퇴하려면 현재암호를 입력하세요 <br />
<label style="display: inline-block; width: 100px;">현재암호</label><input type="password" name="password" /><br />
</form>
</div>
<!-- <div th:replace="~{파일명 :: fragment이름}"></div> -->
<div th:replace="~{footer :: footerFragment}"></div>
</div>
</body>
</html>
- 마이페이지 생성시 데이터가 입력되어 있어야 함
➡️ 마이페이지 생성시 obj로 member 데이터를 불러온다모델 생성
Model model
후model
에 조회한member
데이터를 담아준다if(menu ==1){ Member member = mService.selectMemberOne(userid); model.addAttribute("obj", member); }
- 회원정보 수정 메서드 생성 : 세션에서 가져온 유저 아이디 주면 멤버반환
SESSION_ID
가져와서 userid에 담는다 (회원정보 수정조건)userid
가 존재하는 경우member.setId(userid)
if(menu == 해당메뉴번호)
의 경우에 맞게mService
에member
를 넣어 return값을 반환한다
// 마이페이지 생성
// http://127.0.0.1:8080/ROOT/member/mypage.do
@GetMapping(value = "/mypage.do")
...
if(menu == 1){
Member member = mService.selectMemberOne(userid);
model.addAttribute("obj", member);
}
// http://127.0.0.1:8080/ROOT/member/mypage.do?&menu=1
// 회원정보 수정하기
@PostMapping(value = "/mypage.do")
public String mypagePOST(
@RequestParam(name = "menu") int menu,
@ModelAttribute Member member
){
System.out.println("회원정보수정하기" + member.toString());
// 1,2,3번 모두 session의 id정보가 필요하다
String userid = (String) httpSession.getAttribute("SESSION_ID");
if( userid == null ){
return "redirect:/member/login.do";
}
member.setId(userid);
if( menu == 1 ){ //회원정보 변경시
// 성공하든 실패하든 이동하지 않으니 반환값을 받을 필요 없다
mService.updateMember(member);
return "redirect:/member/mypage.do?menu=1";
} else if( menu == 2 ){ //암호변경시
mService.updateMemberPassword(member);
return "redirect:/member/mypage.do?menu=2";
} else if( menu ==3 ){ //회원탈퇴시
mService.deleteOneMember(member);
return "redirect:/member/mypage.do?menu=3";
}
return "redirect:/member/mypage.do?menu=1";
}
- 회원정보조회 ➡️
String id
가 오면 저장소에서 조회 후 회원 객체 return- 회원정보수정 ➡️
member
가 오면Query
조건에 맞는 데이터 조회 후 변경항목 변경 후short
값 return- 회원삭제(탈퇴) ➡️
member
가 오면Query
조건에 맞는 데이터 조회 후 데이터 삭제 후int
값 return- 암호변경
➡️ 1.member
가 오면this.loginMember
로그인 처리후member
return
2.retmember != null
인 경우getId()
로 일치하는member
데이터를 조회하여 새로운Member retmember1
에 담는다
3. 사용자가 입력한 변경할 비밀번호member.getPassword1
를retmember1
에setPassword
해준다
4. 저장소에 저장후int
형태로 return
😧❓ 암호변경시 로그인 처리, updateMemberPassword 에서 온 멤버는 어디서 넣어줌?
@ModelAttribute Member
에서는 entity 형태로 가져옴
암호 변경시 사용자가 입력한name="password”
name="password1”
데이터 가져오고 entity 형태의 member 가져오지만 나머지는null
비어있는 형태이다
// 회원정보조회
public Member selectMemberOne(String id){
try {
return mRepository.findById(id).orElse(null);
} catch (Exception e) {
e.printStackTrace();;
return null;
}
}
// 회원정보수정
public short updateMember( Member member ){
try {
// 조건
Query query = new Query();
Criteria criteria = Criteria.where("_id").is(member.getId());
query.addCriteria(criteria);
// 이름과 연락처만 변경
Update update = new Update();
update.set("name", member.getName());
update.set("phone", member.getPhone());
// (조건, 변경값, 클래스)
// Class<T> => 타입 => Member.class
UpdateResult result = mongoTemplate.updateFirst(query, update, Member.class);
if(result.getModifiedCount() == 1L){
return 1;
}
return 0;
} catch (Exception e) {
e.printStackTrace();;
return -1;
}
}
// 회원 삭제
public int deleteOneMember( Member member ){
try {
Query query = new Query();
Criteria criteria = Criteria.where("_id").is(member.getId());
query.addCriteria(criteria);
DeleteResult result = mongoTemplate.remove(query, Member.class);
if(result.getDeletedCount() == 1L){
return 1;
}
return 0;
} catch (Exception e) {
e.printStackTrace();;
return -1;
}
}
// 암호변경
public int updateMemberPassword(Member member){
try {
// 보안을 위한 로그인 처리
Member retmember = this.loginMember(member);
if(retmember != null){
// 일치한 경우 암호 수정
//1. 변경하기위한 회원정보 조회 => 변경하려면 기존 회원정보 가져와야함
// id 가져오기
Member retmember1 = mRepository.findById(member.getId()).orElse(null);
// 2. 현재암호를 password1로 변경
retmember1.setPassword(member.getPassword1());
// 3. 다시 저장
mRepository.save(retmember1);
}
return 0;
} catch (Exception e) {
e.printStackTrace();;
return -1;
}
}
mRepository.delete(entity);
다운로드 후 static 안에 다운받은 파일을 넣어준다
<head>
태그 안에 넣고 <body>
에서 <div>
태그로 적용
<link rel="stylesheet" th:href="@{/css/bootstrap.css}" />
<script th:src="@{/js/bootstrap.js}"></script>
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>판매자</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.css}" />
<script th:src="@{/js/bootstrap.js}"></script>
</head>
<body>
<div class="container" style="border : 1px solid #cccccc;">
<a href="@{/item/selectlist.do}">물품조회</a>
<a href="@{/item/insertbatch.do}">일괄추가</a>
</div>
</body>
</html>
💡 <a href="@{/item/selectlist.do}">물품조회</a>
로 작성되어있어 작동하지 않았다
<a th:href="@{/item/selectlist.do}">물품조회</a>
로 수정해주니 오류없이 동작한다
<a href="@{/item/insertbatch.do}">일괄추가</a>
생성model
로 하면 엔티티 형태로 입력이 가능한데 아래와 같은 경우는 같은name
끼리 데이터를 담을 배열을 만들었다 =>name
별 알맞은 형태의 배열로 담는다
Iterable
를 실행했기 때문에 html의 name 값이 중복된다!...
@Controller
@RequestMapping(value = "/item")
public class ItemController {
@Autowired
ItemService itemService;
@GetMapping(value = "/seller.do")
public String sellerGET(){
return "seller"; //seller.html파일
}
// 컨트롤러 생성시 이동된 주소 가져와서 작성하면 편합니다
@GetMapping(value="/insertbatch.do")
public String insertbatchGET() {
return "insertbatch";
}
//
@PostMapping(value="/insertbatch.do")
public String insertbatchPOST(
// 반복자로 출력되기 때문에 다 배열로 온다
@RequestParam(name = "name") String[] name,
@RequestParam(name = "content") String[] content,
@RequestParam(name = "price") Long[] price,
@RequestParam(name = "quantity") Long[] quantity,
@RequestParam(name = "image") MultipartFile[] file) throws IOException {
// 새 배열 생성
List<Item> list = new ArrayList<>();
for( int i=0; i<name.length; i++ ){
Item item = new Item();
item.setName( name[i] );
item.setContent( content[i] );
item.setPrice( price[i] );
item.setQuantity( quantity[i] );
item.setFilename( file[i].getOriginalFilename() );
item.setFilesize( file[i].getSize() );
item.setFiletype( file[i].getContentType() );
item.setFiledata( file[i].getBytes() ); //throws IOException
list.add(item);
}
itemService.insertbatch(list);
return "insertbatch";
}
}
- 반복문으로 입력
th:value="|물품명${i}|”
➡️ 문자 입력시
th:value="${i}+1”
➡️ 숫자 입력시- entity에 맞는 name값 주기
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>물품일괄추가</title>
<script th:src="@{/js/bootstrap.js}"></script>
</head>
<body>
<div>
<!-- 이미지 첨부는 enctype="multipart/form-data" -->
<form th:action="@{/item/insertbatch.do}" method="post" enctype="multipart/form-data">
<!-- 1부터 2까지 반복 => 화면에 2줄 출력된다 -->
<th:block th:each="i : ${#numbers.sequence(1,2)}">
<input type="text" name="name" placeholder="물품명" th:value="|물품명${i}|" />
<input type="text" name="content" placeholder="물품내용" th:value="|내용${i}|" />
<input type="text" name="price" placeholder="물품가격" th:value="${i}+100" />
<input type="text" name="quantity" placeholder="물품수량" th:value="${i}+20" />
<!-- 파일은 엔티티 변수로 맞추면 안된다! 한번에 못들어감 -->
<input type="file" name="image" />
<br />
</th:block>
<hr />
<input type="submit" value="물품일괄추가" />
</form>
</div>
</body>
</html>
<th:block th:each="i : ${numbers.sequence(1,2)}">
numbers
앞에 #
를 입력해주지 않아 발생한 오류였다
<th:block th:each="i : ${#numbers.sequence(1,2)}">
로 변경해주니 오류없이 실행된다
저장소 생성
package com.example.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.example.entity.Item;
public interface ItemRepository extends MongoRepository<Item, Long>{
}
MongoRepository.saveAll(Iterable<S> entities) : List<S>
➡️ 반복자=Iterable<S> entities
라는 말은list
도 가능하다는 뜻이다@Autowired ItemRepository iRepository;
// [저장소] 사용하여 저장
public int insertbatch( List<Item> list ){
try {
List<Item> retlist = iRepository.saveAll(list);
if(retlist.size() == list.size()){
return 1;
}
return 0;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}