ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • board,member(spring.ver1)
    JAVA 2021. 7. 26. 22:30

    TDD(Test Driven Development)

    “테스트 주도 개발(Test-Driven Development)”이라는 용어 그대로 이해해보면, 개발을 하는 데 있어서 테스트가 주가 되어 개발한다는 의미.

    “테스트를 염두에 둔 프로그램 개발 방법”이라고 이해하기 ( 자바-스프링 코드를 테스트로 돌릴 수 있는 도구 )

    1. board_ui

    >> 게시물컨트롤

    @Controller
    public class 게시물컨트롤{
    	@Autowired
    	IBoardDAO boardDAO;
    	
    
    	@GetMapping("board")
    	public String 게시물등록준비하다() {
    		// 요청
    		
    		// 업무
    		
    		// 경로 지정
    		return "게시물등록창";
    	}
    	
    //	@PostMapping("board") 같은 말
    	@RequestMapping(value="board", method=RequestMethod.POST)
    	public ModelAndView 게시물등록하다(Board board, HttpSession session) {//세션필요하면 그냥 매개값으로 Spring 이 알아서 준다.
    		// 요청
    		
    		ModelAndView mv = new ModelAndView();
    		
    		//다른값들은 알아서 들어가지만 회원의 번호는 session 통해서 가져와 넣어놓고 DB에 save 해야한다.
    		int 로그인한회원번호 = (Integer)session.getAttribute("no");
    		Member 작성한회원 = new Member();
    		작성한회원.setNo(로그인한회원번호);
    		board.setWriter(작성한회원);
    		
    		boardDAO.save(board);
    		
    		// 경로지정
    		mv.addObject("title", board.getTitle());
    		mv.setViewName("게시물등록결과통보");
    		return mv;
    	}
    
    }

    2. login_ui

    >> 로그인아웃컨트롤

    @Controller
    public class 로그인아웃컨트롤{
    	
    	@Autowired
    	IMemberDAO dao;
    	
    	@GetMapping("login")//로그인준비, 이름이 겹치니까 method 를 다르게 해서 겹치지 않게 한다. get, post
    	public String process() {
    		
    		return "로그인창";
    	}
    	
    	@PostMapping("faillogin")//로그인준비, 이름이 겹치니까 method 를 다르게 해서 겹치지 않게 한다. get, post
    	public String 실패로그인다시하다() {
    		
    		return "로그인창";
    	}
    
    
    	@PostMapping("login")
    	public ModelAndView process(@RequestParam("id")String id,String password, HttpSession session) {//변수명에 맞춰 getParameter 자동으로 해줌.
    																			//만약 들어오는 name 속성값과 변수명이 다르다면 @RequestParam("name값") 을 앞에 매칭시켜준다.
    		//요청
    		
    		Object[] NoAndName = dao.isin(id, password);
    		
    		ModelAndView mv = new ModelAndView();
    		if(NoAndName==null) {
    			//여기서 조건을 따져 forward 로 보내며 msg 까지 설정하면 로그인준비 control 에서 다시 조건을 따져 request 로 값을 받고 view 넘기지 않아도
    			//이 값들이 request에 누적되어 view까지 넘어간다.
    			mv.addObject("message", "아이디 또는 비밀번호가 일치하지 않습니다.");
    			mv.setViewName("forward:/faillogin");//여긴 redirect 하면 request.setAttribute() 가 안가니까 forward
    			                                     //애초에 faillogin 요청명으로 주소창에 뜨는게 더이상..
    			return mv;
    		}
    		
    		session.setAttribute("no", NoAndName[0]);	// 회원 번호 저장.
    		session.setAttribute("name", NoAndName[1]);	// 회원 번호 저장.
    		session.setMaxInactiveInterval(60*30); 	// s(초) 단위. session이 존재하는 시간. 즉, 로그인 유지시간이다. web.xml 에서도 지정가능하며 거기는 분단위다.
    		mv.setViewName("redirect:/contents");//forward 도 가능하지만 forward대상 컨트롤을 숨기기 때문에 요청이 남아있어 새로고침시 다시 자신 컨트롤이 작동한다.
    											 // redirect 해도 session저장에 문제 없으니 이렇게 하자.
    		
    		return mv;
    	}
    	
    	@RequestMapping("logout")
    	public ModelAndView process(HttpSession session) {
    		if(session != null) {
    			session.invalidate();
    		}
    		
    		ModelAndView mv = new ModelAndView();
    		
    		mv.setViewName("redirect:/contents");
    		return mv;
    	}
    }

    3. member_ui

    >> Profile

    @Controller
    public class Profile{
    
    	IMemberDAO memberDAO = new MemberDAO();
    	
    	@RequestMapping("profile/{no}")//profile?no=1 -> profile/1 로 바뀜
    	public ModelAndView process(@PathVariable int no) {
    		Member member = memberDAO.findByNo(no);
    		ModelAndView mv = new ModelAndView();
    		mv.setViewName("profile");
    		mv.addObject("profile", member.getProfile());
    		return mv;
    	}
    }
    @RequestMapping : {요청명에 들어가는 변하는 값}
    @PathVariable : 요청명의(경로) 변수를 매핑 , 변수명과 설정한 요청명값(현재 no) 가 같지안다면 @PathVariable("no")까지 써야함

    >> 아이디중복검사컨트롤

    @Controller
    public class 아이디중복검사컨트롤{
    	@Autowired
    	MemberDAO dao;
    	
    	@GetMapping("id")
    	public String process() {
    		return "아이디중복검사창";
    	}
    
    	@PostMapping("id")
    	public ModelAndView process(String id) {
    		
    		boolean 사용가능여부 = false;
    		String 메세지 = "";	
    		if(id == "") {
    			 메세지 = "아이디를 입력하세요.";
    		}else if(id != null){
    		사용가능여부 = dao.isin(id);
    		메세지 = (사용가능여부)?"사용가능한 ID입니다.":"이미 사용 중인 ID입니다.";
    		}
    		
    		ModelAndView mv = new ModelAndView();
    		
    		mv.addObject("msg", 메세지);
    		mv.addObject("id", id);
    		mv.addObject("result", 사용가능여부);
    		mv.setViewName("아이디중복검사창");
    		return mv;
    	}
    
    }

    >> 회원등록컨트롤

    @Controller
    public class 회원등록컨트롤{
    	@Autowired
    	MemberDAO memberDAO;
    
    	@GetMapping("member")
    	public String process() {
    		
    		return "회원등록창";
    	}
    	
    	@PostMapping("member")
    	public ModelAndView process(@ModelAttribute Member member,HttpSession session) {//객체 안의 멤버변수를 살펴서 변수명에 맞춰 매칭시켜줌
    		//@ModelAttribute 는 MultipartFile 형의 멤버변수를 가지고 있다면 선언해줘야 한다.
    		
    		ModelAndView mv = null;
    		
    		try {	
    			
    			memberDAO.save(member);
    			
    			mv = new ModelAndView();
    			
    			// 정보는 여기서 저장하고 나가기!
    
    			Object[] NoAndName = memberDAO.isin(member.getId(), member.getPassword());
    			session.setAttribute("no", NoAndName[0]);
    			session.setAttribute("name", NoAndName[1]);
    			
    			// 이 컨트롤에서 벗어나기!
    			mv.setViewName("redirect:/contents");
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    
    		return mv;
    	}
    	
    	@RequestMapping("result_member")
    	public String process2() {
    		
    		
    		return "회원등록결과통보";
    	}
    	
    	@RequestMapping("member/{no}")
    	public ModelAndView process(@PathVariable("no")int 회원번호) {//요청이름과 변수명이 다르니 ("no")까지 써주기
    		ModelAndView mv = new ModelAndView();
    		
    		Member findedMember = memberDAO.findByNo(회원번호);
    			
    		mv.addObject("findedMember", findedMember);
    		mv.setViewName("회원상세창");
    
    		return mv;
    	}
    	
    	@RequestMapping("member2/{no}")
    	public ModelAndView process2(@PathVariable("no")int 회원번호) {
    		ModelAndView mv = new ModelAndView();
    		
    		Member findedMember = memberDAO.findByNo(회원번호);
    			
    		mv.addObject("findedMember", findedMember);
    		
    		//그림을 문자열로 바꿔 간다.
    		String profile = Base64.encodeBytes(findedMember.getProfile());
    		mv.addObject("profile", profile);
    		mv.setViewName("회원상세창2");
    
    		return mv;
    	}
    }
    if문 해석
    1. 첨부리스트.childNodes.length : 첨부리스트 내의 li 들 몇갠지
    2. 첨부리스트.childNodes.length - 1 : 마지막 li의 인덱스
    3. 첨부리스트.childNodes[첨부리스트.childNodes.length - 1].childNodes[0].value : 마지막 li 의 input.file 의 값

    4. VIEW

    >> 게시물등록창 ( 주석설명 참고 )

    <body>
    
    	제목
    	<input type="text" name="title" />
    	<br> 내용
    	<textarea name="title"></textarea>
    	<br> 첨부
    	<br>
    	<ul id="attachlist"></ul>
    	<button onclick="첨부요소추가하다()">추가</button>
    
    </body>
    </html>
    
    <script type="text/javascript">
    	function 첨부요소추가하다(e) {
    		// ul 을 가져온다.
    		var 첨부리스트 = document.querySelector("#attachlist");
    
    		// ul 아래 자식요소(li)가 1개 이상은 있는데, 
    		// 마지막 li의 첫번째 자식인 input.file 에 값이 없을 때 
    		if (첨부리스트.childNodes.length > 0
    				&& 첨부리스트.childNodes[첨부리스트.childNodes.length - 1].childNodes[0].value == "") {
    			return 0;
    		}
    
    		// ul 아래 들어갈 li, input.file, input.button 을 만든다.
    		var li = document.createElement("li");
    		var fileInput = document.createElement("input");
    		fileInput.type = "file";
    		var btnDelete = document.createElement("input");
    		btnDelete.type = "button";
    		btnDelete.value = "삭제";
    
    		// containerLi 라는 속성을 직접 만들어 li 객체를 기억한다.
    		// 과거의 li를 지목해서 가져올려면 어딘가에 저장해야 하기 때문이다.
    		btnDelete.containerLi = li;
    
    		btnDelete.addEventListener("click", function(event) {
    			// 현재 클릭 이벤트가 발생한 btnDelete 의 containerLi속성에 저장한 li를 가져온다.
    			var li=event.target.containerLi;
    			// 저장한 li 정보의 부모인 ul 을 가져온다.
    			var ul=li.parentNode;
    			// ul 의 자식요소인 li 를 지운다.
    			ul.removeChild(li);
    		})
    
    		// li 아래에 input.file, input.button 을 넣는다.
    		li.appendChild(fileInput);
    		li.appendChild(btnDelete);
    
    		// ul 아래에 위의 li 를 넣는다.
    		첨부리스트.appendChild(li);
    
    	}
    </script>

    >> 로그인상단

    <body>
    
    <% if(session!=null){  %>
      <%=(String)session.getAttribute("name") %><i class="fas fa-user"></i><button onclick="location.href='logout'">로그아웃</button>
      <button onclick="location.href='member/<%=(int)session.getAttribute("no")%>'">개인정보</button>
      <button onclick="location.href='member2/<%=(int)session.getAttribute("no")%>'">개인정보/이미지글자형식</button>
    <%}else{ %>
       <button onclick="location.href='login'">로그인</button><!-- get방식. post라고 안써있으면 다 get방식이다. -->
       <button onclick="location.href='member'">회원등록</button>
    <%} %>
    </body>

    >> 로그인창

    <body>
    <h1>로그인</h1>
    <p>${message}</p>
    <form action="login" method="post" onsubmit="return 필수입력()">
    id <input type="text" id="id" name="id"/>
    password <input type="password" id="password" name="password"/>
    <input type="submit" value="로그인"/><input type="button" onclick="location.href='member'" value="회원등록">
    </form>
    </body>

    >> 아이디중복검사창

    <body>
    	<form action="id" method="post">
    		아이디 <input type="text" name="id" id="id" value="${id}" /> <input type="submit" value="검사">
    	</form>
    	<%-- 결과 출력 --%>
    	${msg}
    	<%if(result){ %>
    	<br>
    	<button onclick="id를보내다()">사용</button>
    	<%} %>
    	<button onclick="self.close()">취소</button>
    </body>

    >> 회원등록창

    <form action="member" onsubmit="return 회원등록하다()" method="post" enctype="multipart/form-data">
    // 중략..
    <script>
    /* 우편번호,주소 */
    function 조회창을띄우다(){
    	window.open("prepare_address","","width=450,height=300");
    }
    // 중략..
    /* 아이디 */
    function 아이디중복검사창을띄우다(){
    	window.open("id","","width=450,height=300");
    }

    >> 회원상세창

    <body>
    
    <h1>회원상세</h1>
    성명 <input type = "text" id="name" readonly="readonly" value="${findedMember.name}"><br>
    프로필<img src="/profile/${findedMember.no}"/><br>
    우편번호 <input type = "text" id="post" readonly="readonly" value="${findedMember.post}"><br>
    주소 <input type = "text" readonly="readonly" value="${findedMember.address}"> <br>
    상세주소 <input type = "text" readonly="readonly" value="${findedMember.detailaddress}"> <br>
    전화번호 <input type = "text" readonly="readonly" value="${findedMember.tel}"/><br>
    이메일 <input type = "text" readonly="readonly" value="${findedMember.email}"/><br>
    아이디 <input type = "text" readonly="readonly" value="${findedMember.id}"><br>
    
    <button onclick="location.href='/contents'">내용창으로</button>
    </body>

    5. 상세보기에서 이미지 경로와 다시 내용보기 경로가 오류가 나는 이유

    상세보기 uri = mvc/member/n

    이곳에서 src 또는 href 경로요청을 zz 라고 해놓으면

    src 또는 href 경로요청의 uri = mvc/member/zz 식으로 가기 때문에 요청명이 'zz' 인 곳으로 가려했는데 아니여서 오류가 난다.

    같은 위치에 요청명이 바뀐다. ( mvc/member/n 에서 n 부분)

    요청명이 'member/zz' 인 걸 찾는다.

    해결방법 1. 절대루트로 작성한다.(http://~)

    해결방법 2. ' /' 를 맨앞에 붙이면 ip:port/ 부터 시작한다

    (1) '/'(ip:port/ 다음 경로) (2) '../' ( 윗 경로 ), (3)'' = '../' ( 현재경로 )

    1. tomcat 의 server.xml 에 현재 앱의 path 를 / 로 바꾸기.

     <Context docBase="spring-mvc" path="/" reloadable="true" source="org.eclipse.jst.jee.server:spring-mvc"/></Host>

    앞으로 주소창 요청이 http://localhost:8090/요청명 ( 프로젝트명 이제 안써도된다. )

    2. 이미지를 불러올 src 요청명 맨앞에 '/' 를 붙이면 요청명을 ip:port/member/ 에서 경로를 하나 올라가서 ip:port/ 부터 시작한다.

    ip:port/요청명 로 잘 매핑된다.

    프로필<img src="/profile/${findedMember.no}"/><br>

    'JAVA' 카테고리의 다른 글

    Spring:댓글  (0) 2021.07.26
    Spring:resolver  (0) 2021.07.26
    Spring:Mapping,Repository,Autowired(DI),multipartFile  (0) 2021.07.26
    Spring, Tomcat Servers의xml파일정리  (0) 2021.07.26
    UML: ClassDiagram,SequenceDiagram, Servlet:Filter  (0) 2021.07.26

    댓글

Designed by Tistory.