https://github.com/Five-Sun/ICT_Intern_API_Exercise_Spring
API에 대해 알아보고 Open API를 활용해 보는 개인 프로젝트를 진행했었다.
학교에서 배웠던 Node.js와 Html 등을 활용했었다.
다양한 언어와 프레임워크를 경험해보기 위해서 Java 기반의 Spring로 저번 프로젝트를 리팩토링 해보려고 한다.
1. Spring 기초 강의 영상 시청하기
처음 학습하다 보니 Spring의 동작 원리는 체계적이지만 복잡해 보였다.
물론 Node.js 또한 다양한 기능들을 사용하고자 하면 어렵겠지만 Spring을 처음 접해보다 보니 더욱 어렵게 느껴졌다.
아키텍처 패턴의 한 종류인 MVC패턴은 스프링 프레임워크에서 주로 쓰이는 개념이라고 하는데 정보처리기사에서 공부했었던 기억이 있다.
url요청 정보에 맞는 데이터에 접근할 수 있는 특정 컨트롤러가 동작을 하고 컨트롤러는 자바 파일로 구현할 수 있으며 어노테이션이라는 기능을 통해 지정할 수 있었다. 모든 작업의 시작은 컨트롤러로 부터 시작되는 거 같았다.
기초 강의 영상은 다음 강의를 이용했다.
[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 대시보드 - 인프런 | 강의 (inflearn.com)
2. MySQL 연동 공부하기
Spring Boot에 DB를 연동하는 방법은 의외로 간단했다. Mybatis나 JPA 등을 이용해 편리하게 프로젝트에 DB를 연동할 수 있었다.
- build.gradle 파일에 의존성을 추가해준다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
- application.properties(스프링 부트가 자동으로 로딩하게 되는 규약들)에 DB 정보를 추가한다.
spring.jpa.database=mysql
spring.datasource.url=jdbc:mysql://localhost:3306/openapi?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
이후 필요한 Entity, Repository 등을 추가로 작성하면 DB를 연동한 동작을 수행할 수 있었다.
3. 내 프로젝트에 적용해보기
3.1 Spring boot 이용해 프로젝트 생성하기
Spring initializr을 통해서 편리하게 프로젝트를 생성할 수 있었다.
Gradle Project, Java, Project Metadata를 설정하고 사용할 의존성이나 플로그인 설정 등을 생성하면서 설정을 할 수 있었다.
Generate 된 압축 파일을 해제하면 프로젝트 파일의 틀이 완성되었다. (React와 비슷한 느낌을 받았다.)
3.2 Git 연동하기
편한 형상관리를 위해 Git을 연동하였다.
Intellij IDE의 Git 연동은 간단했다.
- File-Settings에 들어가 Git 실행 파일 경로 지정
- Git 계정 연동
- Repository를 생성하여 사용하거나 생선된 Repository를 불러와 사용.
(상반바에 Git메뉴를 통해 편리하게 Commit/Push 할 수 있었다.)
3.3 이전 프로젝트 가져오기
Git에서 이전 프로젝트 파일은 다운로드 하여 Html, CSS, Javascript파일을 이번 프로젝트 폴더에 추가하였다.
CSS파일이 적용되지 않는 문제가 발생했고 경로 설정을 어떻게 해야하나 고민에 빠졌다.
스프링 부트를 통해 생성한 프로젝트의 경우 static 아래로 resource가 잡히기 때문에 css와 js 디렉토리를 static 아래 생성하고 경로를 재설정하니 css와 js가 정상적으로 작동되었다.
저번 프로젝트와 동일한 포트를 사용하기 위해 application.properties 에 server.port =3000을 입력하여 3000번 포트에서 실행될 수 있도록 수정해보았다.
3.4 Spring으로 적용하기
사실 Node.js를 활용해서 express하고 routing 하는 동작이 전부였다 보니 ProjectController 역할을 하는 java 파일 하나로 Node.js에서 Spring으로 변경할 수 있었다.
@Controller
public class ProjectController {
@GetMapping("/")
public String home() {
return "home";
}
@GetMapping("/kakao")
public String kakao() {
return "kakao";
}
@GetMapping("/data")
public String data() {
return "data";
}
}
이전 프로젝트 기능이 문제없이 잘 동작했다.
3.5 기능 구현하기
이대로만 사용해본다면 아쉽기 때문에 Spring을 좀 더 사용하기 위해서 DB를 연동한 기능을 구현해보자고 생각했다.
- Kakao 책 검색에 사용된 검색어를 DB에 저장하기
DB는 위의 내용과 같이 properties와 build 파일을 수정하고 Search Entity를 생성했다.
@Entity
public class Search {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
private int count = 1;
//getter-setter
Entity를 사용해서 제공할 Service를 위해 SearchRepository 인터페이스와 JPASearchRepository를 작성한다.
public interface SearchRepository {
Search save(Search search);
Optional<Search> findByTitle(String title);
List<Search> findAll();
Search update(Search search);
}
public class JPASearchRepository implements SearchRepository {
private final EntityManager em;
public JPASearchRepository(EntityManager em) {
this.em = em;
}
@Override
public Search save(Search search) {
em.persist(search);
return search;
}
//등등
JPA에서 더 편리하게 사용할 수 있는 스프링 데이터 JPA가 있지만 JPA만 활용해도 충분히 구현할 수 있는 내용이었다.
스프링 데이터 JPA도 추가적으로 학습해보아야겠다.
Entity Manager는 엔티티를 관리하는 역할을 수행하는 클래스이다.
엔티티 매니저 내부에 영속성 컨텍스트라는 걸 두어서 엔티티들을 관리한다.
DB에 직접 접근하는 방식이 아닌 Entity에 접근하여 DB내용을 조작한다고 이해했다.
@Transactional
public class SearchService {
private final SearchRepository searchRepository;
public SearchService(SearchRepository searchRepository) {
this.searchRepository = searchRepository;
}
public void join(Search search) {
try {
validateDuplicateSearch(search);//중복 내용 검증
searchRepository.save(search);
}
catch (IllegalStateException e) {
searchRepository.update(search);
}
}
실제 동작할 Service를 SearchService에 작성하였다.
이렇게 작성된 Service들을 Controller에서 호출하여 사용하는 방식인 거 같다.
- 검색 기록 화면에서 내용 출력하기
@GetMapping("/search")
public String list(Model model) {
List<Search> searches = searchService.findSearch();
model.addAttribute("searches", searches);
return "search";
}
/search가 매핑되면 DB에 있는 모든 데이터들을 List형태로 불러온다.
return 타입이 String인 경우,templates 폴더의 같은 이름의 html파일을 return 한다.
- 중복된 검색어가 검색되면 Insert가 아닌 Update 하기
기초강의를 통해 구현했던 동작 중 중복 데이터가 들어오면 에러를 발생해 중복 데이터가 저장되지 않게 하는 기능이 있었다. 이것을 이용해서 중복된 검색어가 들어오면 count를 증가해주는 기능을 만들어봤다.
private void validateDuplicateSearch(Search search) {
searchRepository.findByTitle(search.getTitle())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 기록입니다.");
});
}
findByTitle을 통해 같은 검색어가 있는지 확인.
있다면 IllegalStateException을 발생.
public void join(Search search) {
try {
validateDuplicateSearch(search);//중복 내용 검증
searchRepository.save(search);
}
catch (IllegalStateException e) {
searchRepository.update(search);
}
}
try-catch문을 사용해서 중복 내용 검증을 하고 Exception인 발생하지 않는다면 save 메소드 호출.
Exception 발생 시 update 메소드 호출.
@Override
public Search update(Search search) {
em.createQuery("update Search s set s.count = (s.count +1) where s.title = :title")
.setParameter("title", search.getTitle())
.executeUpdate();
return null;
}
update SQL문을 다음과 같이 작성하고 parmeter를 form에 입력된 타이틀을 가져와 where절에 조건으로 넣고 count도 entity에 저장된 count값을 가져와 1을 더해 업데이트를 해준다.
-Paging처리
Spring data JPA에서 제공하는 Pageable을 사용하고 싶었지만 findAll()을 쿼리로 작성하여 사용하고 있었기에 Pageable의 객체는 Page타입으로 받아야 하는 문제가 발생했다. 이 점을 해결하기 위해선 위에서 해결한 update부분을 다시 작성해야 하는 문제가 생겨서 RequestParam을 통해서 처리해보았다. RequestParam의 pagenum을 요청해서 setFirstResult를 조작하여 10개씩 화면에 출력하고 있다.
@Override
public List<Search> findAll(int page) {
return em.createQuery("select s from Search s order by s.id desc", Search.class)
.setFirstResult(page)
.setMaxResults(10)
.getResultList();
}
@GetMapping("/search")
public String list(Model model,@RequestParam(required = false,defaultValue = "0", value = "page") int page ) {
model.addAttribute("searches", searchService.findSearch(page));
System.out.println(page);
return "search";
}
느낀 점:
하나의 기능을 만들기 위해서 Controller, Domain, Service, Repository Interface, Repository 등 건들어야하는 많게 느껴졌지만 간단한 프로젝트가 아니라 큰 프로젝트라면? 이 패턴이 익숙해진다면 장점이 확실히 있을 거 같다.
Spring 프레임워크를 현업에서도 많이 쓰는 만큼 Spring Boot를 통해서 정말 다양한 기능을 제공해주는 거 같았다.
모두 처음 접하는 기능과 라이브러리들이었기에 어려웠지만 이 또한 익숙해진다면 복잡하고 어려운 기능들도 간단하게 한줄로 구현할 수 있을 거 같은 생각이 들었다.
Spring이 지원하는 언어와 방식이 다양하기 때문에 이에 대한 공부도 많이 필요할 거 같다.
'ICT 학점연계 프로젝트 인턴십' 카테고리의 다른 글
[ICT 학점연계 프로젝트] 웹 개발 업무 (0) | 2022.05.27 |
---|---|
[ICT 학점연계 프로젝트]셋톱박스형 음악 플레이어 구동 실습 (0) | 2022.04.21 |
[ICT 학점연계 프로젝트] Open API 활용한 웹 _ Node.js (0) | 2022.03.18 |
[ICT 학점연계 프로젝트 인턴십] API란? API에 대해 이해하기 (0) | 2022.03.08 |
[ICT 학점연계 프로젝트 인턴십] Github 연동, 기본적인 조작 익히기 (0) | 2022.03.02 |