ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] 지역별 카페 추천 프로젝트(2)
    스프링 프로젝트 - 카페 만들기 2022. 9. 1. 11:18

    여행할 때 방문하면 좋을 카페를 추천하는 웹 프로젝트

     

    📌 데이터베이스를 활용해서 화면 구성하기

     

    이제 저장한 데이터베이스에서 데이터들을 불러와서 화면을 구성하도록 처리할 것이다.

     

    일단 DTO를 사용할 것이다.

    DTO는 Data Transfer Object로, 데이터 전달을 목적으로 하고, 일회성으로 데이터를 주고받는 용도이다.

    반면, Entity 객체는 실제 데이터베이스와 관련있으며, 일회성 사용이 아닌 엔티티 매니저가 관리하는 객체이다.

     

    DTO를 사용할 경우 엔티티 객체를 DTO객체로 변환하거나, DTO객체를 엔티티 객체로 변환하는 과정이 필요하다는 단점이 있다.

     

    이제 서비스 계층에서 DTO로 파라미터 리턴타입을 처리하도록 구성할 것이다.

     

    📍 우선 DTO를 만든다.

    package yeonjy.cafe.dto;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public class AreaDTO {
        
        private Long gno;
        private String local;
    }

    📎 @Data

    Getter/Setter, toString(), equals(), hashCode() 를 자동으로 생성함

     

    위 코드는 앞서 설명한 것과 같이 Area 클래스 코드와 유사하다.

     

     

     

    📍이제 서비스 계층을 만들 것이다.

     

    서비스 계층을 만드는 이유는 프레젠테이션 계층(Controller등)과 데이터 엑세스 계층이 직접적으로 통신하지 않게 하기 위함이다.

     

    아래와 같이 AreaService 인터페이스와 AreaSericeImpl 클래스를 생성한다.

     

    AreaService 인터페이스

    package yeonjy.cafe.service;
    
    import yeonjy.cafe.dto.AreaDTO;
    import yeonjy.cafe.entity.Area;
    
    public interface AreaService {
    
        Long register(AreaDTO dto);
    
        AreaDTO read(Long gno);
    
        default Area dtoToEntity(AreaDTO dto) {
            Area entity = Area.builder()
                    .gno(dto.getGno())
                    .local(dto.getLocal())
                    .build();
            return entity;
        }
    
        default AreaDTO entityToDto(Area entity) {
    
            AreaDTO dto = AreaDTO.builder()
                    .gno(entity.getGno())
                    .local(entity.getLocal())
                    .build();
            return dto;
        }
    
    
    }

    register과 read 메소드를 만들고,

    default로 dto ↔ entity 를 해주는 메소드를 구현한다.

     

    AreaServiceImpl 클래스

    package yeonjy.cafe.service;
    
    
    import lombok.RequiredArgsConstructor;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.stereotype.Service;
    import yeonjy.cafe.dto.AreaDTO;
    import yeonjy.cafe.entity.Area;
    import yeonjy.cafe.repository.AreaRepository;
    
    import java.util.Optional;
    
    @Service
    @Log4j2
    @RequiredArgsConstructor // 의존성 자동 주입
    public class AreaServiceImpl implements AreaService{
    
        private final AreaRepository repository;
    
        @Override
        public Long register(AreaDTO dto) {
    
            log.info("DTO 정보 -------------------------------");
            log.info(dto);
    
            Area entity = dtoToEntity(dto);
    
            log.info(entity);
    
            repository.save(entity);
    
            return entity.getGno();
    
        }
    
        @Override
        public AreaDTO read(Long gno) {
    
            Optional<Area> result = repository.findById(gno);
    
            return result.map(this::entityToDto).orElse(null);
        }
    
    }

    register과 read메소드를 구현했다.

     

    이제 register과 read메소드가 잘 작동하는지 test코드를 작성하여 확인해 보겠다.

    package yeonjy.cafe.service;
    
    import org.assertj.core.api.Assertions;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import yeonjy.cafe.dto.AreaDTO;
    import yeonjy.cafe.entity.Area;
    import yeonjy.cafe.repository.AreaRepository;
    
    
    @SpringBootTest
    public class AreaServiceTests {
    
        @Autowired
        private AreaService areaService;
    
        @Autowired
        private AreaRepository areaRepository;
    
        @Test
        public void 등록확인() {
            
            AreaDTO areaDTO = AreaDTO.builder()
                    .gno(5L)
                    .local("부산")
                    .build();
    
            areaService.register(areaDTO);
            
            Area area = areaRepository.findById(5L).get();
            Assertions.assertThat(area).isEqualTo(areaDTO);
        }
    
    }

    위 코드를 실행했더니 querydsl 관련 오류가 계속 떴다.

    그래서 gradle에서 querydsl 관련 설정을 했다.

     

    수정된 build.gradle

    buildscript {
        ext {
            querydslVersion = '5.0.0'
        }
    }
    
    plugins {
        id 'org.springframework.boot' version '2.7.3'
        id 'io.spring.dependency-management' version '1.0.13.RELEASE'
        id 'java'
        id 'war'
        id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
    }
    
    group = 'yeonjy'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '17'
    
    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        compileOnly 'org.projectlombok:lombok'
        developmentOnly 'org.springframework.boot:spring-boot-devtools'
        annotationProcessor 'org.projectlombok:lombok'
        providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    
        implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client'
    
        implementation "com.querydsl:querydsl-jpa:${querydslVersion}"
        implementation "com.querydsl:querydsl-apt:${querydslVersion}"
    }
    
    tasks.named('test') {
        useJUnitPlatform()
    }
    
    def querydslDir = "$buildDir/generated/querydsl"
    
    querydsl {
        jpa = true
        querydslSourcesDir = querydslDir
    }
    
    sourceSets {
        main.java.srcDir querydslDir
    }
    
    configurations {
        querydsl.extendsFrom compileClasspath
    }
    
    compileQuerydsl {
        options.annotationProcessorPath = configurations.querydsl
    }

     

    다시 test 를 실행했더니 아래와 같은 오류가 발생했다.

    친절하다.. 

    그런데 DTO로 등록해서 dtoToEntity를 했으니까 entity에만 잘 등록되어있다는 것을 확인하면 된다.

    따라서 test코드를 수정했다.

     

    사실 HeidiSQL으로 확인은 했지만.. 조금이라도 더 TDD 스럽게 프로그래밍하기 위해 코드로 확인해 보겠다.

     

    @Test
    public void 등록확인() {
    
        AreaDTO areaDTO = AreaDTO.builder()
                .gno(5L)
                .local("부산")
                .build();
    
        areaService.register(areaDTO);
    
        Area area = areaRepository.findById(5L).get();
    
        Assertions.assertThat(area.getGno()).isEqualTo(areaDTO.getGno());
        Assertions.assertThat(area.getLocal()).isEqualTo(areaDTO.getLocal());
    }

    일단 하나씩 확인해서 결과를 보았다.

    다행히 테스트도 통과했다.

     

     

    이제 화면에도 DB에서 꺼내서 보여주도록 한다.

     

    CafeController 클래스

    @Controller
    @RequestMapping("/cafe")
    @RequiredArgsConstructor
    
    public class CafeController {
    
        private final AreaRepository areaRepository;
    
        @GetMapping({"/start"})
        public void start(Model model) {
    
            List<Area> areas = areaRepository.findAll();
    
            model.addAttribute("areas", areas);
    
        }
    }

     

    start.html 수정

    <div class="container">
        <div class="row">
            <div th:each="areas : ${areas}">
                <div class="col-sm-11">
                    <h3>[[${areas.local}]]</h3>
                    <p>[[${areas.local}]]에는 어떤 카페가 있는지 확인하세요!</p>
                </div>
            </div>
        </div>
    </div>

     

     

     

Designed by Tistory.