Spring 강의 day 3

주세환·2023년 5월 1일
0

Spring

목록 보기
3/18
post-thumbnail

ItemImage

Itemlist

Item

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Item {
    	
	private long no;
	private String name;
	private String content; //clob
	private long price;
	private long quantity;
	private Date regdate ;
	
	private long imageNo; // 대표이미지 번호를 저장할 임시변수
}

dto에 Item.java를 생성한다.


ItemMapper

@Mapper
public interface ItemMapper {

    // 물품전체조회
    public List<Item> selectItemList();
    
}

mapper폴더에 ItemMapper를 생성하고

resources - mappers 폴더에 itemMapper.xml에 itemMapper.xml을 생성하여 작성한다.

빨간색과 파란색은 서로 명이 동일해야 한다.


ItemService

// 컨트롤러에서 실행하는 클래스
@Service
public interface ItemService {
    
    // 물품 전체 조회
    public List<Item> selectItemList();
}

example 아래에 service 폴더에 ItemService.java를 생성하고

ItemServiceImpl

같은 위치에 ItemServiceImpl.java를 생성한다.


ItemController

@Controller
@RequestMapping(value ="/item")
public class ItemController {

    @Autowired ItemService iService; // 서비스 객체 생성

    // 127.0.0.1/9090/ROOT/item/selectlist.do
    @GetMapping(value = "/selectlist.do")
    public String selectListGET( Model model ){
        // 1. 서비스를 호출하여 물품목록받기
        List<Item> list = iService.selectItemList();

        // 2. model을 이용하여 view로 받은 목록 전달하기
        model.addAttribute("list", list);

        // 3. view를 화면에 표시하기
        return "/item/selectlist"; // resources/templates/item 폴더 생성 selectlist.html을 생성
    }

}

controller폴더에 ItemController.java를 생성한다


selectlist.html

    <title>물품등록</title>
</head>
<body>
    <table>
        <thead>
            <tr>
                <th>물품번호</th>
                <th>물품명</th>
                <th>물품내용</th>
                <th>물품가격</th>
                <th>물품수량</th>
                <th>등록일</th>
            </tr>
        </thead> 
        <tbody>
            <tr th:each="obj : ${list}">
                <td th:text="${obj.no}"></td>
                <td th:text="${obj.name}"></td>
                <td th:text="${obj.content}"></td>
                <td th:text="${obj.price}"></td>
                <td th:text="${obj.quantity}"></td>
                <td th:text="${obj.regdate}"></td>
                <td><a th:href="@{/item/insertimage.do(no=${obj.no})}">이미지등록</a></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

templates - item 폴더에 selectlist.html을 생성하여 작성한다.

  • controller에서 list에 담아서 보내기 때문에 html에서 list로 받아야 한다.

입력한 주소로 이동하면 db에 저장되어 있는 itemlist의 정보가 나열된다.

이미지 등록을 눌렀을 때


InsertItemimage

ItemController(GET)

public class ItemController {

    @Autowired ItemService iService; // 서비스 객체 생성

    // /item/insertimage.do?no=11 => name값은 no이고 value값은 11 숫자가 전달
    // <input type="text" name="no" value="11" />
    @GetMapping(value ="/insertimage.do")
    public String insertimageGET(
        @RequestParam(name="no", defaultValue ="0", required = false) long no,
        Model model ) {
        if( no == 0 ){
            return "redirect:selectlist.do"; // 상대경로로 이동, 가장 마지막 주소만 변경해서 이동
        }
        model.addAttribute("itemno", no);
        return "/item/insertimage"; // resources/templates/item폴더/insertimage.html
    }

기존에 작성하였던 ItemController.java에 insertimageGET을 추가한다.


ItemImage

@Getter
@Setter
@ToString(exclude = {"filedata"})
@NoArgsConstructor
@AllArgsConstructor
public class ItemImage {

	private long no;
    
	private String filename;
	private long filesize;
	private byte[] filedata; //blob
	private String filetype;

	private long itemno;
	private Date regdate;
}

dto에 ItemImage.java를 추가한다.


insertimage.html

<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>
    <form th:action="@{/item/insertimage.do}" method="post" enctype="multipart/form-data">
        외래키(물품번호) : <input type="text" name="itemno" th:value="${itemno}"/><br />
        첨부할 이미지 : <input type="file" name="file1" accept="image/*"/><br />
        <input type="submit" value="업로드" />
    </form>
</body>
</html>

insertimage.html를 작성하면

작성한 대로 화면이 나오게 된다.

파일선택을 누르면 이렇게 사진을 선택할 수 있다.

default.png를 선택한 모습.

Controller에 post를 만들지 않았기 때문에 405 에러가 뜬다.


ItemController(POST)

public class ItemController {

    @Autowired ItemService iService; // 서비스 객체 생성

    // 파일은 dto에 자동으로 추가되지 않음. 수동으로 추가.
    @PostMapping(value = "/insertimage.do")
    public String insertImagePOST( @ModelAttribute ItemImage obj,
        @RequestParam(name = "file1") MultipartFile file1) throws IOException {
        obj.setFilename( file1.getOriginalFilename() );
        obj.setFilesize( file1.getSize() );
        obj.setFiletype( file1.getContentType() );
        obj.setFiledata( file1.getBytes() ); // exception 발생됨.


        System.out.println(obj.toString());

        return "redirect:insertimage.do?no=" + obj.getItemno();
    }

ItemController에 insertImagePOST를 추가한다.

...

</form>
<a th:href="@{/item/selectlist.do}">물품 목록으로</a>

insertimage.html에 물품 목록으로 가는 a태그를 추가한다.

정상적으로 추가된다.

insertimage

public String insertImagePOST( @ModelAttribute ItemImage obj,
    @RequestParam(name = "file1") MultipartFile file1) throws IOException {
    obj.setFilename( file1.getOriginalFilename() );
    obj.setFilesize( file1.getSize() );
    obj.setFiletype( file1.getContentType() );
    obj.setFiledata( file1.getBytes() ); // exception 발생됨.
    System.out.println(obj.toString()); // 확인용

    int ret = iService.insertItemImageOne(obj);
    if( ret == 1 ){
        return "redirect:insertimage.do?no=" + obj.getItemno();
    }
    return "redirect:insertimage.do?no=" + obj.getItemno();
}

ItemController에 위 항목을 추가한다.


ItemMapper

// 물품 이미지 등록
public int insertItemImageOne(ItemImage obj);

insertItemImageOne을 추가하고


ItemService

public int insertItemImageOne(ItemImage obj);

ItemService에도 똑같이 추가한다.


ItemServiceImpl

@Override
public int insertItemImageOne(ItemImage obj) {
    try{
        return iMapper.insertItemImageOne(obj);
    }
    catch(Exception e) {
        e.printStackTrace();
        return -1;
    }
}

ItemServiceImpl도 마찬가지로 추가하고

itemMapper.xml

...

    <insert id="insertItemImageOne" parameterType="com.example.dto.ItemImage">
        INSERT INTO itemimage(filename, filesize, filetype, filedata, itemno)
        VALUES(#{filename}, #{filesize}, #{filetype}, #{filedata}, #{itemno})
    </insert>
</mapper>

itemMapper.xml에 SQL문을 추가한다.


파일을 선택하고 업로드를 누르면

이미지 등록하는 주소로 다시 이동하고

정상적으로 등록이 된다.


SelectItemimageOne

ItemMapper

// 이미지 번호가 전송되면 1개의 이미지 정보 반환
public ItemImage selectItemImageOne(long no);

// 물품 번호를 전송하면 해당하는 이미지 번호 n개를 반환
public List<Long> selectItemImageNo(long itemno);

ItemMapper.java에 위 코드를 추가한다.


itemMapper.xml

<select id="selectItemImageOne" parameterType="long" resultType="com.example.dto.ItemImage">
    SELECT * FROM itemimage WHERE no = #{no}
</select>

<select id="selectItemImageNo" parameterType="long" >
    SELECT no FROM itemimage WHERE itemno = #{itemno}
</select>

itemMapper.xml에도 위 코드를 추가한다.


ItemController

@Autowired ItemMapper iMapper; // 서비스를 사용하지 않고 매퍼 호출(일반적이지 않음.)
@Autowired ResourceLoader resourceLoader; // resources폴더의 파일을 읽기 위한 객체 생성

// <img src="@{/item/image(no=1)}">
// String = html파일을 표시
// ResponseEntity<byte[]> => 이미지, 동영상 등을 표시
// 127.0.0.1:9090/ROOT/item/image?no=1
@GetMapping(value = "/image")
public ResponseEntity<byte[]> image(@RequestParam(name = "no", defaultValue = "0") long no) throws IOException{
    ItemImage obj = iMapper.selectItemImageOne(no);
    HttpHeaders headers = new HttpHeaders(); // import org.springframework.http.HttpHeaders;

    if( obj != null ){ // 이미지가 존재할 경우
        if(obj.getFilesize() > 0){
            headers.setContentType( MediaType.parseMediaType( obj.getFiletype() ) );
            return new ResponseEntity<>( obj.getFiledata(), headers, HttpStatus.OK);
        }
    }
    
    // 이미지가 없을 경우
    InputStream is = resourceLoader.getResource("classpath:/static/images/default.png").getInputStream(); // exception 발생됨.
    headers.setContentType(MediaType.IMAGE_PNG);
    return new ResponseEntity<>( is.readAllBytes(), headers, HttpStatus.OK);
}

ItemController에 위 코드를 추가하고

resources-images 폴더에 default.png를 넣어주고 실행한다.

이미지가 존재할 경우 해당 이미지가 나온다.

이미지가 존재하지 않을 경우 지정한 default.png가 나온다.


global.properties

resources 폴더에 global.properties 파일을 생성하고

# 개발자가 필요해서 만드는 환경설정 파일

default.image=claspath:/static/images/default.png
board.pagetotal=10

위처럼 작성한다.


// classpath => resources와 동일함.
@PropertySource(value = {"classpath.global.properties"}) // 직접만든 환경설정파일 위치

Boot20230427Application.java에 추가한다.


@Value("${default.image}") String defaultImage; // import org.springframework.beans.factory.annotation.Value;

...

// 이미지가 없을 경우
    InputStream is = resourceLoader.getResource(defaultImage).getInputStream(); // exception 발생됨.
    headers.setContentType(MediaType.IMAGE_PNG);
    return new ResponseEntity<>( is.readAllBytes(), headers, HttpStatus.OK);
}

ItemController를 위처럼 추가, 수정한다.


# dto위치설정
mybatis.type-aliases-package=com.example.dto

application.properties에 위 코드도 추가한다.

com.example.dto.~~ 대신 ~~만 입력할 수 있다.


SelectItemimageNo

ItemController

@GetMapping(value = "/insertimage.do")
public String insertimageGET(
    @RequestParam(name="no", defaultValue ="0", required = false) long no,
    Model model ) {
    if( no == 0 ){
        return "redirect:selectlist.do"; // 상대경로로 이동, 가장 마지막 주소만 변경해서 이동
    }
    
    // 현재 물품에 해당하는 이미지 번호
    List<Long> imgNo = iMapper.selectItemImageNo(no);
    System.out.println("insertimage.do => " + imgNo.toString());
    
    model.addAttribute("imgno", imgNo);
    model.addAttribute("itemno", no);
    return "/item/insertimage"; // resources/templates/item폴더/insertimage.html
}

ItemController의 insertimageGET을 위처럼 수정한다.


<body>
    <form th:action="@{/item/insertimage.do}" method="post" enctype="multipart/form-data">
        외래키(물품번호) : <input type="text" name="itemno" th:value="${itemno}" readonly /><br />
        첨부할 이미지 : <input type="file" name="file1" accept="image/*"/><br />
        <input type="submit" value="업로드" />
    </form>
    <a th:href="@{/item/selectlist.do}">물품 목록으로</a>

    <hr />
    <div th:each="tmp : ${imgno}" style="display:inline-block">
         <img th:src="@{/item/image( no=${tmp} )}" style="width:50px; height:50px" />
    </div>
</body>

insertimage.html을 위처럼 수정하여 사진이 나올 곳을 생성한다.

물품번호에 해당하는 이미지가 모두 나오는 것을 확인할 수 있다.


Member

join

dto

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Member {
	
	private String id;
	private String password;
	private String newpw;
	private String name;
	private int age;
	private Date regdate ; 	
	private String role; // 고객 customer, 판매자 seller
}

dto 폴더에 Member.java를 생성한다.


MemberController

@Controller
@RequestMapping(value = "/member")
@Slf4j // 디버깅
public class MemberController {
    
    @Autowired MemberMapper mMapper; // 매퍼객체 생성하기

    @GetMapping(value ="/join.do")
    public String joinGET(@ModelAttribute Member obj){
        log.info("member=> {}", "joinGET");
        // 디버깅
        return "/member/join";
    }

    @PostMapping(value="/join.do")
    public String joinPOST( @ModelAttribute Member obj ){
        log.info("join.do POST => {}", obj.toString());
        // 여기서 매퍼 호출 후 회원가입하기
        int ret = mMapper.insertMemberOne(obj);
        if( ret == 1 ){
            // 127.0.0.1:9090/ROOT/member/home.do
            return "redirect:/home.do"; // 성공시 홈으로
        }
        // return "redirect:/member/join.do"; // 실패시 회원가입으로
        return "redirect:join.do"; // 실패시 회원가입으로
    }

MemberController를 생성하여 위 코드를 작성하고


join.html

<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>
    <form th:Action="@{/member/join.do}" method ="post">
        아이디 : <input type="text" name="id" autofocus/><br />
        암호 : <input type="text" name="password" value="a"/><br />
        이름 : <input type="text" name="name" value="b"/><br />
        나이 : <input type="number" name="age" value="1"/><br />
        권한 : <input type="text" name="role" value="CUSTOMER" /><br />
        <input type="submit" value="회원가입" />
    </form>
</body>
</html>

join.html을 생성한다


MemberMapper

@Mapper
public interface MemberMapper {
    
    public int insertMemberOne(Member member);
    
}

MemberMapper에 추가하고

memberMapper.xml

<mapper namespace="com.example.mapper.MemberMapper">
    <insert id="insertMemberOne" parameterType="com.example.dto.Member">
        INSERT INTO member(id, password, name, age, role)
        VALUES( #{id}, #{password}, #{name}, #{age}, #{role})
    </insert>
</mapper>

memberMapper도 일치하게 작성한다.


Login

login.html

<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>
    <form th:Action="@{/member/login.do}" method ="post">
        아이디 : <input type="text" name="id" autofocus/><br />
        암호 : <input type="text" name="password" value="a"/><br />
        <input type="submit" value="로그인" />
    </form>
</body>
</html>

login.html을 생성한다.


MemberController

@GetMapping(value="/login.do")
public String loginGET(){
    return "/member/login";
}

@PostMapping(value="/login.do")
public String loginPOST(@ModelAttribute Member member){
    log.info("login.do => {}", member.toString());
    int ret = mMapper.selectMemberOne(member);
    if( ret == 1 ){
        return "redirect:/home.do"; // 로그인 성공 시
    }
    return "redirect:/login.do"; // 로그인 실패 시
}

loginGET, loginPOST를 작성한다.


MemberMapper

public Member selectMemberOne(Member member);

MemberMapper에 selectMemberOne을 추가한다.


memberMapper.xml

<select id="selectMemberOne" parameterType="com.example.dto.Member" resultType="com.example.dto.Member">
    SELECT m.id, m.name, m.age, m.role FROM member m WHERE m.id = #{id} AND m.password = #{password}
</select>

memberMapper.xml에도 일치하게 작성한다.


MemberController

@RequiredArgsConstructor

...

final MemberMapper mMapper; // 매퍼객체 생성하기
final HttpSession httpSession;

...

@GetMapping(value="/login.do")
public String loginGET(){
    return "/member/login";
}

@PostMapping(value="/login.do")
public String loginPOST(@ModelAttribute Member member){
    log.info("login.do => {}", member.toString()); // view에서 잘 전송되었는지
    Member ret = mMapper.selectMemberOne(member); // 로그인한 사용자의 정보 반환
    if( ret != null ){
        log.info("login1.do => {}", ret.toString());
        // 세션에 2개의 정보 아이디와 이름 추가하기 (기본시간 30분)
        // 다른 페이지에서 세션의 아이디가 존재하는지 확인 후 로그인 여부 판단
        httpSession.setAttribute("USERID", ret.getId());
        httpSession.setAttribute("USERNAME", ret.getName());
        return "redirect:/home.do"; // 로그인 성공 시
    }
    return "redirect:/login.do"; // 로그인 실패 시
}

이렇게 수정한다.


회원가입창

회원가입 후 home.do로 이동

로그인창

로그인 성공시 home.do로 이동


db로 세션관리

<!-- DB로 세션관리 -->
<dependency>
	<groupId>org.springframework.session</groupId>
	<artifactId>spring-session-jdbc</artifactId>
</dependency>

pom.xml에 dependency를 하나 추가한다.


# db로 세션관리
server.servlet.session.timeout=3600
spring.session.store-type=jdbc
spring.session.jdbc.initialize-schema=always

application.properties에 위 코드를 추가한다.

이렇게 하면 로그인 정보를 세션에 저장할 수 있다.


크롬과 엣지로 각각 로그인을 한 상태이다. DB를 확인해보면 2가지의 정보가 들어가있는 것을 볼 수 있다.


logout

MemberController

// GET, POST가 같은 동작을 함
@RequestMapping(value="/logout.do", method = {RequestMethod.GET, RequestMethod.POST})
public String logoutPOST(){
    httpSession.invalidate(); // 세션의 정보를 다 지움
    return "redirect:/home.do";
}

세션의 정보를 다 지우는 로그아웃 기능을 추가하였다.


home.html

<body>
    <h3>홈 화면</h3>

    <a th:href="@{/board/insert.do}">게시판 글쓰기</a>

    <div th:if="${session.USERID == null}">
        <a th:href="@{/member/login.do}">로그인</a>
        <a th:href="@{/member/join.do}">회원가입</a>
    </div>

    <div th:if="${session.USERID != null}">
        <p th:text="|${session.USERNAME} 님 로그인|"></p>
        <a th:href="@{/member/logout.do}">로그아웃</a>
    </div>
</body>

home.html을 수정한다.

세션에 로그인 정보가 없으면 로그인과 회원가입, 정보가 있으면 로그인 된 정보의 이름을 불러오고 로그아웃으로 이어지는 a태그를 추가한다.


로그인을 하지 않았을 때

로그인했을 때.


SELLER

MemberController

httpSession.setAttribute("USERID", ret.getId());
httpSession.setAttribute("USERNAME", ret.getName());
httpSession.setAttribute("ROLE", ret.getRole());

ROLE도 추가해주고


home.html

<div th:if="${session.USERID != null}">
    <p th:text="|${session.USERNAME} 님 로그인|"></p>
    <a th:href="@{/member/logout.do}">로그아웃</a>
    <div th:if="${#strings.equals(session.ROLE, 'SELLER')}">
        <a th:href="@{/seller/home.do}">판매자 홈</a>
    </div>
</div>

ROLE의 String이 SELLER와 같을 경우 판매자 홈으로 이동할 수 있도록 수정하였다.


판매자로 로그인한 상태이다.

판매자 홈을 누른 상태.

seller/home.do를 만들지 않았으니 404 오류가 뜨는 것이 당연하다.

0개의 댓글