2시간의 타임어택 프로젝트 -명함관리 프로그램을 만들자

Yellta·약 22시간 전
0

Projects

목록 보기
6/6

2시간의 타임어택 프로젝트 -명함관리 프로그램을 만들자

부스트코스 링크
명함관리 CLI기획서를 보고 Spring Web으로 만들어보자!

Spring 의존성이다.
Spring Data JPA, Web ,타임리프, 롬복을 추가해준다.

Application.properties에 필요한 정보를 저장하자!

spring.application.name=businesscard  
  
  
# H2 데이터베이스 연결 설정  
spring.datasource.url=jdbc:h2:tcp://localhost/~/businesscard  
spring.datasource.driverClassName=org.h2.Driver  
spring.datasource.username=sa  
spring.datasource.password=

Database로는 H2 DB를 선택했다. 고른 이유는 특별하지 않고 그냥 당장 쓸 수 있어서 선택함

h2 콘솔에 URL로 DB를 만들어준다. 참고로 안되면

User폴더에서 mv.db파일 만들어주면 된다. 나는 안돼서 test.mv.db복붙하고 이름 바꿔서 수행함

도메인 설계

다급함이 보이는 글씨

최종(이었던) backend 패키지 , 파일 목록

2시간 타임어택 시간에 내가 작성한 패키지 + 파일 목록인데 이 글을 쓰면서 나중에 구조를 조금 바꿧다

내가 작성한 순서대로 설명하겠다

BusinessCardService

public interface BusinessCardService {  
  
    public void saveCard(BusinessCard businessCard);  
    public List<BusinessCard> findBusinessCardByName(String name);  
  
  
}

BusinesesCardService interface이다. 사실 구현체가 하나이기때문에 굳히 역할은 없어도 되지만 역할과 구현을 쌍으로 생각하라고 했기 때문에 작성했다.

BusinessCardRepository

@Repository  
public interface BusinessCardRepository extends JpaRepository<BusinessCard, Long> {  
    List<BusinessCard> findByName(String name);  
    public List<BusinessCard> findByNameContaining(String name);  
  
}

DB와 연결 역할을 하는 Repository
여기서 findByNameContaining SQL의 %Like%과 같은 기능을 한다. 즉 포함하는 것을 return해준다.
리턴하는 값을 List에 넣어 반환해준다.

참고로 findByName은 Name과 정확히 일치하는 항목을 찾는 것이다.

BusinessCardServiceImpl

@Service  
@RequiredArgsConstructor  
@Transactional  
public class BusinessCardServiceImpl implements BusinessCardService{  
    private final BusinessCardRepository businessCardRepository;  
    @Override  
    public void saveCard(BusinessCard businessCard) {  
        businessCardRepository.save(businessCard);  
    }  
    @Override  
    public List<BusinessCard> findBusinessCardByName(String name) {  
        List<BusinessCard> cardList = businessCardRepository.findByName(name);  
        return cardList;  
    }  
}

서비스 구현체
BusinessCardRepository에 의존한다. 하는 역할은 간단하다.
명함 등록과 검색기능을 제공한다.

다음 Controller를 보기전...

Controller를 만들기 전 html파일을 먼저 만들었다.

<!DOCTYPE html>  
<html lang="en" xmlns:th="http://www.thymeleaf.org">  
<head>  
    <meta charset="UTF-8">  
    <title>hello</title>  
    <link rel="stylesheet" href="css/main.css">  
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"  
          integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">  
</head>  
<body>  
<div class="container">  
    <div class="content">  
        <div class ="contentsBox">  
            <form action="/register" method="post">  
                <div class="form-group">  
                    <label for="name">이름</label>  
                    <input type="text" class="form-control" name = "name" id="name" aria-describedby="name" placeholder="이름을 입력하세요" required>  
                </div>  
                <div class="form-group">  
                    <label for="phone">전화번호</label>  
                    <input type="tel"  pattern="\d{11}" maxlength="11" title="전화번호는 숫자 11자리를 입력해야 합니다." class="form-control"  name = "phone" id="phone" placeholder="전화번호" required>  
                </div>  
                <div class="form-group">  
                    <label for="companyName">회사이름</label>  
                    <input type="text" class="form-control" name = "companyName" id="companyName" placeholder="회사이름" required>  
                </div>  
                <div>                    <button type="submit" class="btn btn-primary">명함등록</button>  
                </div>  
  
            </form>  
        </div>  
        <div class="contentsBox">  
            <form action="/search" method="post">  
                <div class="form-row align-items-center">  
                    <div class="col-auto">  
                        <label class="sr-only" for="searchName">이름 검색</label>  
                        <input type="text" class="form-control mb-2" name = "searchName" id="searchName" placeholder="김동동씨" required>  
                    </div>  
                    <div class="col-auto">  
                        <button type="submit" class="btn btn-primary mb-2">검색하기</button>  
                    </div>  
                </div>  
            </form>  
        </div>  
  
</div>  
</body>  
</html>

정적 파일이 될 index.html파일 css는 부트스트랩이랑 가운데 정렬을 위해서 내가 만든거 두 개뿐이다.

여기서 집중해야할 부분은

<div class="form-group">  
    <label for="name">이름</label>  
    <input type="text" class="form-control" name = "name" id="name" aria-describedby="name" placeholder="이름을 입력하세요" required>  
</div>

form태그 안에서 input태그 안이다. 여기서 name이라는 속성을 사용하는데 이는 Spring에서 데이터를 가져올 때 필요하다. 해당 부분은 스터디가 필요한 부분이라서 지금은 간략하게 표현하겠다.

그럼 해당 POST메소드에 맞는 Controller를 확인해보자

RegisterController

@Controller  
@Slf4j  
@RequiredArgsConstructor  
public class RegisterController {  
    private final BusinessCardService businessCardService;  
  
    @PostMapping("/register")  
    public String registerBusinessCard(  
            @RequestParam("name") String name,  
            @RequestParam("phone") String phone,  
            @RequestParam("companyName") String companyName,  
            Model model  
  
    ){  
        BusinessCard businessCard = new BusinessCard(name,phone,companyName, LocalDateTime.now());  
        businessCardService.saveCard(businessCard);  
  
        //model , 컨트롤러에서 ㅊ ㅓ리한 데이터(모델, 객체) 를 뷰 템플릿에 넘기기 위해 사용된다.  
        // 주로 타임리프, JSP같은 뷰 템플릿 엔진에서 데이터를 활용해 THML에 동적으로 표시한다.  
        //model객체는 키-값 쌍으로 데이터를 저장하고, 템플릿 엔진에서 해당 키를 통해 값을 참조  
        model.addAttribute("message", "명함이 성공적으로 등록되었습니다!");  
        return "index"; // 성공 메시지를 보여주면서 다시 메인 페이지로 이동  
    }  
  
}

여기서 사용되는게 Model 객체인데 Spring MVC에서 사용한다. @RequestParam으로 name속성을 지정한 값들을 가져온다.

나머지는 그냥 저장해주는 기능..

SearchController

@Slf4j  
@RequiredArgsConstructor  
public class SearchController {  
    private final BusinessCardRepository businessCardRepository;  
  
    @PostMapping("/search")  
    public String seacrhRestul(@RequestParam("searchName") String searchName, Model model){  
//        List<BusinessCard> results = businessCardRepository.findByName(searchName);  
  
        List<BusinessCard> results = businessCardRepository.findByNameContaining(searchName);  
  
        model.addAttribute("results", results);  
        model.addAttribute("searchName", searchName);  
  
        return "searchList";  
    }

검색도 마찬가지이다.
String으로 값을 리턴하게 되면 Spring이 view 템플릿을 templates폴더에서 찾는다!
model은 view와 스프링 데이터를 이어주는 역할을 한다.

근데 여기서 한 가지 문제점

나는 RegisterController, SearchController를 따로따로 구분했다. 하지만 두 가지 기능은 모두 명함관련 기능으므로 하나의 Controller에서 관리하는 것이 좋다고 생각했다.

그리고 심지어 SearchController는 Repository에 service가 아닌 Repository에 의존하고 있었다.

참고로 이 부분은 글 쓰다가 발견해서 얼른 리팩토링해줬다.

@Controller  
@Slf4j  
@RequiredArgsConstructor  
public class BusinessCardController {  
  
    private final BusinessCardService businessCardService;  
  
    @PostMapping("/register")  
    public String registerBusinessCard(  
            @RequestParam("name") String name,  
            @RequestParam("phone") String phone,  
            @RequestParam("companyName") String companyName,  
            Model model  
  
    ){  
        BusinessCard businessCard = new BusinessCard(name,phone,companyName, LocalDateTime.now());  
        businessCardService.saveCard(businessCard);  
  
        //model , 컨트롤러에서 ㅊ ㅓ리한 데이터(모델, 객체) 를 뷰 템플릿에 넘기기 위해 사용된다.  
        // 주로 타임리프, JSP같은 뷰 템플릿 엔진에서 데이터를 활용해 THML에 동적으로 표시한다.  
        //model객체는 키-값 쌍으로 데이터를 저장하고, 템플릿 엔진에서 해당 키를 통해 값을 참조  
        model.addAttribute("message", "명함이 성공적으로 등록되었습니다!");  
        return "index"; // 성공 메시지를 보여주면서 다시 메인 페이지로 이동  
    }  
  
  
    @PostMapping("/search")  
    public String seacrhRestul(@RequestParam("searchName") String searchName, Model model){  
//        List<BusinessCard> results = businessCardRepository.findByName(searchName);  
  
        List<BusinessCard> results = businessCardService.findBusinessCardByName(searchName);  
  
        model.addAttribute("results", results);  
        model.addAttribute("searchName", searchName);  
  
        return "searchList";  
    }

BusinessCardController하나에 명함관련 처리를 다 넣어줬다. 또한 Repository에 의존하고 있떤 searchResult를 Service에 의존하도록 변경해줬다.


출처/참고

깃헙 코드 링크

내 머리 + 챗 지피티

profile
Yellta가 BE개발해요! 왜왜왜왜왜왜왜왜왜왜왜왜왜왜왜왜왜왜왜 가 제일 중요하죠

0개의 댓글