<어려웠던 점 & 해결방법>

1. 기간

   : 생각보다 구현해야할 게 많아 개인적으로 시간이 많이 부족했습니다.

2. 서버 구축

   (1) Spring boot : 스프링 프로젝트를 한지 오래되서 개념을 많이 잊어버려 온라인 강의를 통해 공부 후 개발하였습니다.

   (2) JPA : JPA를 한번도 사용해보지 않아서 마찬가지로 온라인 강의를 통해 공부 후 개발 진행하였습니다.

   (3) 테스트 코드 : 테스트 코드를 한번도 사용해보지 않았어서 추가적으로 공부가 필요했습니다. 하지만 JPA로 엔티티 구성할 때 테스트 코드를 통해 제대로 맞게 한건지, 맞게 동작하는지 확인해볼 수 있어서 진행할 때 많은 도움이 되었습니다.

   (4) 화면 : 프론트 개발 자체를 많이 해보지 않아서 다른 프론트 프레임워크를 사용하기엔 시간 상 너무 부족했고, thymeleaf 템플릿도 거의 처음 사용해봤던 거라 구글링하며 사용 방법을 익혔습니다. 기본적인 html, javascript로만 구현하려니 화면 단에 데이터를 저장하지 못해서 똑같은 데이터를 서버와 계속 주고받는 로직으로 구현해야했고 이로 인해 불필요하게 호출 되는 서비스가 있어서 성능이 저하될 수 있다는 가능성을 파악했지만 다른 방법을 찾기엔 시간 내에 불가능할 것 같아 그대로 진행하게 되었습니다. 

 

 이러한 어려웠던 점들로 인해 챌린지 진행 기간동안 많이 힘들었지만 배운 것도 너무 많고 스스로 어디가 부족한지, 무엇을 더 공부해야할 지 방향을 정할 수 있어서 너무나 많은 도움이 되었습니다. 

 

 기간 내에 완벽하게 100% 구현한게 아니라서 이후에 계속 구현하지 못한 기능들과 UI를 보충, 구현할 계획입니다.

 

'개인 프로젝트' 카테고리의 다른 글

3 - (5). 주요 코드 - View  (0) 2022.01.23
3 - (4). 주요 코드 - Controller  (0) 2022.01.23
3 - (3). 주요 코드 - Service  (0) 2022.01.23
3 - (2). 주요 코드 - Repository  (0) 2022.01.23
3 - (1). 주요 코드 - 엔티티  (0) 2022.01.23

 View 구현은 시간 부족으로 인해 다른 기술을 사용하지 못하고 Spring boot에서 제공하는 thymeleaf 템플릿을 사용하여 구현하였다. 화면 중 메인 화면과 추가로 사용된 기술 위주로 정리하였다.

 

(1) 메인 화면

    카테고리 선택 기능위해 ajax를 사용하여 서버 단으로 데이터를 전달하는 방법으로 구현하였다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="js/all.js"></script>
<div class="container">
    <div>
        <p th:text="${member.nickName}"></p> <p th:text="${member.location.address}"></p>
        <a href="/mains/register">게시물 등록</a> <br>

        <select th:name="category" onchange="clickOption()">
            <option th:each="category : ${allCategory}" th:value="${category.categoryName}" th:text="${category.categoryName}"></option>
        </select>

        <table id="resultTable">
            <thead>
            <tr>
                <th>상품이름</th>
                <th>가격</th>
                <th>좋아요수</th>
                <th>내용</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="product : ${products}" >
                <td>
                    <a th:href="@{/products/view(productId=${product.productId})}">
                        [[${product.productName}]]
                    </a>
                </td>
                <td th:text="${product.price}"></td>
                <td th:text="${product.likeCount}"></td>
                <td th:text="${product.contents}"></td>
            </tr>
            </tbody>
        </table>

        <br><br>

        <button onclick="location.href='/members/myPage'">마이페이지</button>

    </div>
</div>
</body>
</html>
function clickOption() {

    var category = $("select[name=category] > option:selected").val();

    var param = {"category": category}

   $.ajax({
        url: "/",
        contentType: "application/json",
        data: JSON.stringify(param),
        type: "POST",
   }).done(function (fragment) {
        $('#resultTable').replaceWith(fragment);
   });

}

 

(2) 회원가입 화면

    지역 검색 기능 위해 ajax를 이용해 비동기 통신 하였다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="/js/getAllLocations.js"></script>
<div class = "container">

    <form action="/members/new" method="post">
        <div class = "form-group">
            <label for="name">이름</label>
            <input type="text" id="name" name="name" placeholder="이름을 입력하세요"><br>

            <label for="email">이메일</label>
            <input type="email" id="email" name="email" placeholder="email을 입력하세요"><br>

            <label for="password">비밀번호</label>
            <input type="password" id="password" name="password" placeholder="비밀번호를 입력하세요"><br>

            <label for="phoneNumber">전화번호</label>
            <input type="text" id="phoneNumber" name="phoneNumber" placeholder="핸드폰 번호를 입력하세요"><br>

            <label for="nickName">닉네임</label>
            <input type="text" id="nickName" name="nickName" placeholder="닉네임을 입력하세요"><br>

            <label for="location">지역</label>
            <input type="text" id="location" name="location" onkeyup="filter()" placeholder="지역을 입력하세요"><br>
            <select id="resultLocationList" name="locations" onchange="setting()">
                <option th:each="location : ${locationList}" th:value="${location.address}" th:text="${location.address}">
                </option>

            </select>

        </div>
        <br>
        <button type="submit">등록</button>
    </form>
</div>

</body>
</html>
function filter() {

    var address = $("#location").val();

   $.ajax({
        url: "/test",
        data : {
        "address" : address
        },
        type: "POST",
   }).done(function (fragment) {

      $('#resultLocationList').replaceWith(fragment);
     });

}

function setting() {

    var location = $("select[name=locations] > option:selected").val();

    $("#location").val(location);
}

 

(3) 상품 게시물 수정 화면

    화면 로드 시 수정 전 게시물 내용 세팅되게 구현하였다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div class="container">
    <br>
    <form action="/" method="get">
        <button type="submit">홈</button>
    </form>
    <br>
    <form action="/products/update" method="post">
        <div class = "form-group">
            <label for="productName">제목</label>
            <input type="text" id="productName" name="productName" placeholder="제목"><br>

            <label>카테고리</label>
            <select th:name="category">
                <option th:each="category : ${allCategory}" th:value="${category.categoryName}" th:text="${category.categoryName}"></option>
            </select><br>

            <select name="status">
                <option name="st" value="RESERVATION" text="RESERVATION">예약중</option>
                <option name="st" value="SALE" text="SALE">판매중</option>
                <option name="st" value="COMPLETE" text="COMPLETE">거래완료</option>
            </select>

            <label for="price">가격</label>
            <input type="text" id="price" name="price" placeholder="가격"><br>

            <label for="contents">내용</label>
            <input type="text" id="contents" name="contents" placeholder="내용"><br>

            <input type="hidden" name="productId" th:value="*{product.productId}">
        </div>
        <br>
        <button type="submit">수정</button>
    </form>
    <br>
    <form action="/members/myPage">
        <input type="submit" value="마이페이지">
    </form>
</div>
<script type="text/javascript">
    $(document).ready(function() {
        $('#productName').val('[[${product.productName}]]')
        $('#price').val('[[${product.price}]]')
        $('#contents').val('[[${product.contents}]]')
    });
</script>
</body>
</html>

'개인 프로젝트' 카테고리의 다른 글

4. 정리  (0) 2022.01.23
3 - (4). 주요 코드 - Controller  (0) 2022.01.23
3 - (3). 주요 코드 - Service  (0) 2022.01.23
3 - (2). 주요 코드 - Repository  (0) 2022.01.23
3 - (1). 주요 코드 - 엔티티  (0) 2022.01.23

Controller에서는 앞서 구현한 엔티티, 레포지토리, 서비스를 가지고 화면과 직접적으로 데이터를 주고 받는 기능들을 담당하기 때문에 경우에 따라 처리해줘야 할 부분이 많았다.  각 Controller에서 주요 기능의 코드, 로직에 대해 정리해보았다.

 

(1) HomeController

@GetMapping("/") // 로그인 정보 저장위해 세션에 저장
    public String home(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember, Model model) {

        if(loginMember == null) {
            return "home";
        }

        // 메인 페이지에서 필요할 데이터 model에 담아서 view에 전달
        // 소속 지역에 해당하는 상품 목록
        List<Product> products = productService.findByLocation(loginMember.getLocation()).orElseGet(ArrayList::new);
        model.addAttribute("products", products);

        // 카테고리 설정을 위한 모든 카테고리 정보
        List<Category> allCategory = categoryService.findAll();
        model.addAttribute("allCategory", allCategory);

        // 계정 정보
        model.addAttribute("member", loginMember);

        return "mains/mainPage";
    }

 

(2) MainController

@PostMapping("/") // 상품 조회 시 카테고리 설정 기능 메소드
    public String setCategory(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER_LOCATION, required = false) Location loginLocation, Model model, @RequestBody Map<String, String> map) {

        String category = map.get("category");
        Category category1 = categoryService.findByName(category).get();

        // 설정한 카테고리에 해당하는 상품 목록들
        List<Product> products = productService.findByLocationAndCategory(loginLocation, category1).orElseGet(ArrayList::new);
        model.addAttribute("products", products);

        return "mains/mainPage :: #resultTable";
    }

    @GetMapping("/mains/register") 
    public String productRegisterForm(Model model) {

        // '게시물 등록' 시 카테고리 설정 위해 데이터 전송
        model.addAttribute("allCategory", categoryService.findAll());

        return "mains/productForm";
    }


    @PostMapping("/mains/register") // 게시물(상품) 등록 메소드
    public String register(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember, ProductForm productForm,
                           @RequestParam(defaultValue = "/mains/mainPage") String redirectURL ) {
        Product product = new Product();

        Category category = categoryService.findByName(productForm.getCategory()).get();

        Location location = locationService.find(loginMember.getLocation().getLocationId()).get();

        product.setProductName(productForm.getProductName());
        product.setPrice(productForm.getPrice());
        product.setCategory(category);
        product.setContents(productForm.getContents());
        product.setMember(loginMember);
        product.setProductStatus(ProductStatus.SALE);
        product.setLocation(location);
        product.setTime(fomatDate());

        productService.register(product);

        return "redirect:/";
    }

    // 게시시간 설정
    public String fomatDate() {
        Date now = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd, HH:mm");
        return format.format(now);
    }

 

(3) MemberController

    // 지역 검색 기능 메소드
    // 키워드로 검색하면 키워드가 포함된 지역 목록이 조회될 수 있도록 구현
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public String getAll(Model model, @RequestParam("address") String address) {

        if(address.length() == 0) { // 키워드가 없을 때 처리
            model.addAttribute("locationList", null);
        }else {
            model.addAttribute("locationList", locationService.findByName(address).orElseGet(ArrayList::new));
        }

        return "members/createMemberForm :: #resultLocationList";
    }

    // 회원가입 기능 메소드
    @PostMapping("/members/new")
    public String create(MemberForm memberForm) {
        Member member = new Member();

        member.setName(memberForm.getName());
        member.setEmail(memberForm.getEmail());
        member.setPassword(memberForm.getPassword());
        member.setPhoneNumber(memberForm.getPhoneNumber());
        member.setNickName(memberForm.getNickName());

        // 화면에서 String형으로 지역 이름을 받아오기 때문에 서비스 처리 필요
        // Optional형이지만 드롭다운 리스트에서 선택했기 때문에 무조건 지역 정보 DB 테이블에 존재
        Location location = locationService.findByName(memberForm.getLocation()).get().get(0);
        member.setLocation(location);

        memberService.join(member);

        return "redirect:/";
    }
    
    // 로그인 기능
    @PostMapping("/members/login")
    public String login(@ModelAttribute @Validated MemberForm memberForm,
                        BindingResult bindingResult, @RequestParam(defaultValue = "/") String redirectURL,
                        HttpServletRequest request) {

        if(bindingResult.hasErrors()) {
            return "members/loginForm";
        }

        Member loginMember = memberService.login(memberForm.getEmail(), memberForm.getPassword()); // 로그인 계정 세팅, memberForm에 지역 값 입력하는 거 추가해줘야함

        if(loginMember == null) {
            bindingResult.reject("loginFail", "아이디 또는 비밀번호가 일치하지 않습니다.");
            return "members/loginForm";
        }

        // 로그인 시 필요 데이터 설정
        productService.findByMember(loginMember).ifPresent(loginMember::setProducts);
        dealService.findByMember(loginMember).ifPresent(loginMember::setDeals);
        interestedService.findInterestedByMember(loginMember).ifPresent(loginMember::setInterestedProducts);

        Location location = locationService.find(loginMember.getLocation().getLocationId()).get();
        loginMember.setLocation(location);

        HttpSession session = request.getSession(); // 세션 있으면 반환, 없으면 신규 세션 생성
        session.setAttribute(SessionConstants.LOGIN_MEMBER, loginMember); // 세션에 로그인 회원 정보 보관
        session.setAttribute(SessionConstants.LOGIN_MEMBER_LOCATION, location);

        return "redirect:/";
    }

 

(4) ProductController

// 상품 세부 조회 기능
    @GetMapping("/products/view")
    public String viewProduct(Model model, Long productId) {

        Product product = productService.find(productId).orElseGet(Product::new);
        model.addAttribute("product", product);

        Member productOwner = product.getMember();
        List<Product> productList = productService.findByMember(productOwner).orElseGet(ArrayList::new);
        productList.remove(product); // 상품 등록자의 또다른 상품 목록에 중복으로 띄워져 삭제
        model.addAttribute("otherProducts", productList);

        model.addAttribute("productOwner", productOwner);

        return "products/view";
    }

    // 상품 댓글 조회 기능
    @GetMapping("/products/comment")
    public String viewComment(Model model, Long productId) {

        Product product = productService.find(productId).orElseGet(Product::new);
        List<Comment> comments = commentService.findByProduct(product).orElseGet(ArrayList::new);

        model.addAttribute("comments", comments);
        model.addAttribute("product", product);

        return "products/comment";

    }

    // 상품 댓글 남기기 기능
    @PostMapping("/products/comment")
    public String registerComment(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember, CommentForm commentForm,
                                  Long productId, Model model) {

        Product product = productService.find(productId).orElseGet(Product::new);

        Comment comment = new Comment();
        
        // 이미 댓글 단 사용자라면 업데이트 되게 구현
        Optional<Long> existId = commentService.exist(loginMember, product);
        existId.ifPresent(comment::setCommentId);

        comment.setContents(commentForm.getContents());
        comment.setMember(loginMember);
        comment.setProduct(product);

        commentService.register(comment);

        return "redirect:/products/comment?productId=" + productId;

    }

    // '모두 보기' 기능
    @GetMapping("/products/all")
    public String all(Model model, String nickName, Product product) {

        Member member = memberService.findByNickName(nickName).orElseGet(Member::new);

        List<Product> products = productService.findByMember(member).orElseGet(ArrayList::new);
        model.addAttribute("products", products);

        model.addAttribute("member", member);
        model.addAttribute("product", product);

        return "products/all";
    }

    // 진행상태별 조회 기능(거래중, 거래완료)
    @PostMapping("/products/all")
    public String allByStatus(Model model, StatusProductForm statusProductForm) {

        List<Product> list = new ArrayList<>();

        String status = statusProductForm.getStatus();
        Long memberId = Long.parseLong(statusProductForm.getMemberId());

        Member member = memberService.find(memberId).orElseGet(Member::new);

        if(status.equals("ALL")) {
            list = productService.findByMember(member).orElseGet(ArrayList::new);
        }else if(status.equals("SALE")) {
            list = productService.findByMemberAndStatus(member, ProductStatus.SALE).orElseGet(ArrayList::new);
        }else { // 거래완료(COMPLETE) 인 경우
            for(Deal deal : member.getDeals()) {
                list.add(deal.getProduct());
            }
        }

        model.addAttribute("products", list);
        model.addAttribute("member", member);

        return "products/all";
    }

 

(5) MyPageController

// 닉네임 수정 기능
    @PostMapping("/mine/profile")
    public String change(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember,
                          Model model, String nickName) {

        loginMember.setNickName(nickName);
        memberService.join(loginMember); // 업데이트

        // 수정된 닉네임 바로 보이게 데이터 전달
        model.addAttribute("nickName", loginMember.getNickName());

        return "mine/profile";

    }

    // 마이페이지 - 판매상품 조회 기능
    @PostMapping("/mine/myProductList")
    public String getProductList(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember,
                                 String status, Model model) {

        List<Product> list = new ArrayList<>();

        if(status.equals("SALE")) { // 판매중
            list = productService.findByMemberAndStatus(loginMember, ProductStatus.SALE).orElseGet(ArrayList::new);
        }else if(status.equals("COMPLETE")){ // 거래완료
            for(Deal deal : loginMember.getDeals()) {
                list.add(deal.getProduct());
                System.out.println(deal.getProduct());
            }
        }


        model.addAttribute("products", list);

        return "mine/myProductList";
    }

    // 마이페이지 - 판매상품 - 진행단계 변경 기능 
    // 현재 진행단계 변경 기능 및 삭제 기능 제대로 동작 안되고 있음
    // 화면에서 상품id가 다르게 전달되어 기능 100% 동작 안됨
    // 시간 부족하여 수정완료하지 못했고 추후에 수정 예정
    @PostMapping("/updateStatus")
    public String updateStatus(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember,
                               String updateStatus, Long productId, Model model) {

        List<Product> list = new ArrayList<>();

        Product product = productService.find(productId).get();

        setNewStatus(loginMember, product, updateStatus);

        if(updateStatus.equals("RESERVATION")) {
            list = productService.findByMemberAndStatus(loginMember, ProductStatus.SALE).orElseGet(ArrayList::new);
        }else {
            for(Deal d : loginMember.getDeals()) {
                list.add(d.getProduct());
            }
        }

        model.addAttribute("products", list);

        return "mine/myProductList";

    }

	// 마이페이지 - 관심상품 기능
    // 시간 부족으로 관심 기능 구현하지 못해 현재 빈 화면만 조회
    // 추후 수정 예정
    @PostMapping("/mine/myInterestedList")
    public String getInterestedList(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember,
                                    Model model, String status) {

        ArrayList<InterestedProduct> list = new ArrayList<>();
        if(status.equals("SALE")) {
            for(InterestedProduct p : loginMember.getInterestedProducts()) {
                if(p.getProduct().getProductStatus().equals(ProductStatus.SALE)) {
                    list.add(p);
                }
            }
        }else {
            for(InterestedProduct p : loginMember.getInterestedProducts()) {
                if(p.getProduct().getProductStatus().equals(ProductStatus.COMPLETE)) {
                    list.add(p);
                }
            }
        }

        model.addAttribute("interestedProducts", list);

        return "mine/myInterestedList";

    }

    // 상품 게시물 수정 기능
    @GetMapping("/products/update")
    public String findUpdateProduct(Model model, Long productId) {

        // 카테고리 목록 데이터 화면에 전달
        model.addAttribute("allCategory", categoryService.findAll());
        model.addAttribute("product", productService.find(productId).get());

        return "products/update";
    }

    // 상품 게시물 수정 기능
    @PostMapping("/products/update")
    public String update(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember, Model model, ProductForm productForm) {

        Product product = productService.find(productForm.getProductId()).get();
        Category category = categoryService.findByName(productForm.getCategory()).get();

        product.setProductName(productForm.getProductName());
        product.setCategory(category);
        product.setPrice(productForm.getPrice());
        product.setContents(productForm.getContents());

        setNewStatus(loginMember,product, productForm.getStatus());

        model.addAttribute("status", "SALE");

        return "mine/myProductList";
    }

    // 진행단계 변경 기능으로 인해 중복되는 코드 메소드로 추출
    public void setNewStatus(@SessionAttribute(name = SessionConstants.LOGIN_MEMBER, required = false) Member loginMember,
                             Product product, String status) {

        if(status.equals("DELETE")) {
            dealService.findByProduct(product).ifPresent(deal -> {
                deal.setProduct(null);
                dealService.remove(deal);
            });
            productService.remove(product);
            return;
        }

        // 거래완료 -> 예약중, 판매중으로 변경 시 거래 내역 삭제
        if(product.getProductStatus().equals(ProductStatus.COMPLETE)) {
            dealService.findByProduct(product).ifPresent(deal -> {
                deal.setProduct(null);
                dealService.remove(deal);
            });

        }

        if(status.equals("RESERVATION")) {
            product.setProductStatus(ProductStatus.RESERVATION);
            productService.register(product); // 업데이트

        }else if(status.equals("SALE")) {
            product.setProductStatus(ProductStatus.SALE);
            productService.register(product); // 업데이트

        }else {
            product.setProductStatus(ProductStatus.COMPLETE);
            Product result = productService.register(product);

            Deal deal = new Deal();
            deal.setMember(loginMember);
            deal.setProduct(result);

            dealService.register(deal);

        }
    }

'개인 프로젝트' 카테고리의 다른 글

4. 정리  (0) 2022.01.23
3 - (5). 주요 코드 - View  (0) 2022.01.23
3 - (3). 주요 코드 - Service  (0) 2022.01.23
3 - (2). 주요 코드 - Repository  (0) 2022.01.23
3 - (1). 주요 코드 - 엔티티  (0) 2022.01.23

Service는 Repository의 구현체로 Repository와 거의 비슷하다. 각 엔티티에 필요한 기본적인 메소드(save, find, delete)들을 구현하고 추가적으로 필요한 메소드들을 구현했다. 

 

<Member>

package com.project.karrot.service;

import com.project.karrot.domain.Member;
import com.project.karrot.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

@Transactional
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public Long join(Member member) {

        validateDuplicateMember(member); // 닉네임 중복 회원 검증

        memberRepository.save(member);

        memberRepository.flush();

        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByNickName(member.getNickName())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 닉네임입니다.");
                });
    }

    public Optional<Member> find(Long memberId) {
        return memberRepository.findById(memberId);
    }

    public Optional<Member> findByName(String name) {
        return memberRepository.findByName(name);
    }

    public Optional<Member> findByNickName(String nickName) {
        return memberRepository.findByNickName(nickName);
    }

    public Member login(String email, String password) { // 이메일 - 패스워드 검증
        return memberRepository.findByEmail(email)
                .filter(member -> member.getPassword().equals(password))
                .orElse(null); }

    public List<Member> findMembers() {
        return memberRepository.findAll();
    }

    public void remove(Member member) {
        memberRepository.delete(member);
    }


}

'개인 프로젝트' 카테고리의 다른 글

3 - (5). 주요 코드 - View  (0) 2022.01.23
3 - (4). 주요 코드 - Controller  (0) 2022.01.23
3 - (2). 주요 코드 - Repository  (0) 2022.01.23
3 - (1). 주요 코드 - 엔티티  (0) 2022.01.23
2. ER Diagram  (0) 2022.01.23

 Repository는 Entity에 의해 생성된 DB 테이블에 접근하는 인터페이스로, 챌린지 조건에 맞게 JPA 기술을 사용하기 위해서 JpaRepository를 상속받아서 구현했다. 

 Spring boot JPA에서 기본적으로 제공해주는 메소드 외에 필요한 메소드들을 추가로 구현했다.

 가장 주 엔티티인 Member와 Product만 첨부하였다. 

 

(1) Member

package com.project.karrot.repository;

import com.project.karrot.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query( "select m from Member m where m.name = :name" )
    Optional<Member> findByName(@Param("name") String name);

    @Query( "select m from Member m where m.nickName = :nickName")
    Optional<Member> findByNickName(@Param("nickName") String nickName);

    @Query( "select m from Member m where m.email = :email")
    Optional<Member> findByEmail(@Param("email") String email);
}

 

(2) Product

package com.project.karrot.repository;

import com.project.karrot.domain.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Map;
import java.util.Optional;

public interface ProductRepository extends JpaRepository<Product, Long> {

    @Query(" select p from Product p where p.location = :location")
    Optional<List<Product>> findByLocation(@Param("location")Location location);

    @Query(" select p from Product p where p.location = :location and p.category = :category")
    Optional<List<Product>> findByLocationAndCategory(@Param("location") Location location, @Param("category") Category category); // 지역별 및 카테고리별 상품 조회

    @Query(" select p from Product p where p.member = :member")
    Optional<List<Product>> findByMember(@Param("member") Member member); // 회원 등록 상품 전체 조회

    @Query(" select p from Product p where p.member = :member and p.productStatus = :status")
    Optional<List<Product>> findByMemberAndStatus(@Param("member") Member member, @Param("status") ProductStatus status); // 회원별 진행단계별 조회


}

'개인 프로젝트' 카테고리의 다른 글

3 - (4). 주요 코드 - Controller  (0) 2022.01.23
3 - (3). 주요 코드 - Service  (0) 2022.01.23
3 - (1). 주요 코드 - 엔티티  (0) 2022.01.23
2. ER Diagram  (0) 2022.01.23
1. 서론 - 시작 계기, 진행 방향  (0) 2022.01.23

 엔티티에서 가장 중요한 부분은 연관관계 설정이라고 생각했다. JPA를 사용했기 때문에 일반적으로 외래키를 필드로 넣어주는게 아닌 객체 자체를 필드로 넣었다. 이로 인해 객체 간 연관관계를 맺고 있기에 CRUD 이벤트 발생 시 그에 맞게 연관관계 처리를 잘 해줘야 한다. 

 코드를 다 첨부하기엔 너무 길어서 주요 메소드 부분만 정리하였다.

 

(1) Member

public void setLocation(Location location) {
        // 기존 지역 정보 삭제
        if(this.location != null) {
            this.location.getMembers().remove(this);
        }
        this.location = location;
        location.getMembers().add(this); // 연관관계 추가
    }

 

(2) Product

    public void setProductStatus(ProductStatus productStatus) {
        if(this.productStatus != null && this.productStatus.equals(ProductStatus.COMPLETE)) { // 거래완료에서
            if(productStatus.equals(ProductStatus.SALE) || productStatus.equals(ProductStatus.RESERVATION)) { // 예약중 or 판매중으로 변경 시
                this.deal.setProduct(null); // 해당 상품 거래 연관관계 삭제
            }
        }

        this.productStatus = productStatus;
    }
    
    public void setMember(Member member) {
        // 등록된 상품의 등록자가 바뀔 일은 없음.
        // 초기 등록자 설정만 필요
        this.member = member;
        this.member.getProducts().add(this);
    }

    public void setCategory(Category category) {
        if(this.category != null) {
            this.category.getProducts().remove(this); // 예전 카테고리의 상품목록에서 제거
        }
        this.category = category; // 새롭게 설정될 카테고리 설정
        category.getProducts().add(this);
    }


    public void setLocation(Location location) {
        if(this.location != null) {
            this.location.getProducts().remove(this);
        }
        this.location = location; // 등록자의 지역으로 상품 지역 설정
        this.location.getProducts().add(this);
    }

 

(3) Comment

public void setProduct(Product product) {
        if(product == null) { // 연관관계 삭제
            this.product.getComments().remove(this);
        }else {
            if(this.product != null) {
                this.product.getComments().remove(this);
            }
            this.product = product;
            product.getComments().add(this);
        }
    }

(4) Deal

 요구사항 중에 상품의 진행 단계를 자유롭게 바꿀 수 있었다. 그에 의해 처리해줘야 할 경우의 수가 조금 더 많았다.

public void setProduct(Product product) {
    this.product = product;
    if(product == null) { // 상품 자체 삭제 또는 상품 진행 단계 변경(거래완료 -> 예약중 or 거래완료 -> 판매중)으로 인한 연관관계 삭제
        //this.product.setDeal(null); // 상품 - 거래 연관관계 삭제
        this.member.getDeals().remove(this); // 회원 - 거래 연관관계 삭제
    }else {
        this.product.setDeal(this);
        this.member.getDeals().add(this); // 회원의 거래 목록에 추가
    }
}

'개인 프로젝트' 카테고리의 다른 글

3 - (4). 주요 코드 - Controller  (0) 2022.01.23
3 - (3). 주요 코드 - Service  (0) 2022.01.23
3 - (2). 주요 코드 - Repository  (0) 2022.01.23
2. ER Diagram  (0) 2022.01.23
1. 서론 - 시작 계기, 진행 방향  (0) 2022.01.23

(1) 테이블

     : Member(회원), Product(상품), Location(지역), Category(카테고리), Comment(댓글), Deal(거래), Interested_Product(관심상품), Image_files(이미지) => 총 8개의 테이블로 구성

     

 

 개인 프로젝트를 해야겠다는 생각 도중에 넘블이라는 챌린지 운영 프로그램을 우연히 발견하게 되어 백엔드 챌린지를 지원하게 되었다.

 

 (1) 주제 : 초기 당근마켓 시스템 구축하기

 (2) 요구사항(기능)

 

 (3) 사용 기술 스택 및 조건

(4) 기간

     : 2022년 1월 7일 ~ 2022년 1월 23일

 

 

(5)

() 정리

    챌린지 커뮤니티에서 제공한 기획서와 요구사항, 조건들을 바탕으로 17일간의 프로젝트를 진행했지만 100% 완성한 것은 아니어서 추후에 개인적으로 더 발전시켜 나갈 예정이다.

'개인 프로젝트' 카테고리의 다른 글

3 - (4). 주요 코드 - Controller  (0) 2022.01.23
3 - (3). 주요 코드 - Service  (0) 2022.01.23
3 - (2). 주요 코드 - Repository  (0) 2022.01.23
3 - (1). 주요 코드 - 엔티티  (0) 2022.01.23
2. ER Diagram  (0) 2022.01.23

+ Recent posts