춤추는 개발자

[Spring boot] IntelliJ와 gradle로 시작하는 Spring Boot 본문

Developer's_til/스프링 프레임워크

[Spring boot] IntelliJ와 gradle로 시작하는 Spring Boot

Heon_9u 2021. 7. 7. 18:02
728x90
반응형

 

 

 

 이번 포스팅부터 Spring Boot와 AWS로 혼자 구현하는 웹서비스를 진행하며 개인 프로젝트를 하나 완성할 예정입니다.

 

 기존에는 VS Code만 사용하며 Java코드를 작성했지만, 이번에는 많은 IT회사(네이버, 카카오, 라인, 쿠팡 등)에서 공식 IDE로 사용하는 IntelliJ를 사용합니다. 아직 교재만 따라가며 IntelliJ를 사용했지만, 개발에 유용한 단축키와 Git과의 연동 덕분에 만족하고 있습니다. 실제로 개발자들이 이야기하는 IntelliJ의 장점은 아래와 같습니다.

 

  • 강력한 추천 기능(Smart Completion)
  • 훨씬 다양한 리팩토링과 디버깅 기능
  • 이클립스의 Git에 비해 훨씬 높은 자유도
  • 프로젝트를 시작할 때, 인덱싱하여 파일을 비롯한 자원들에 대해 빠른 검색 속도
  • HTML과 CSS, JS, XML에 대한 강력한 기능 지원
  • Java, Spring-boot 버전업에 맞춘 빠른 업데이트 
    • 자바 개발에 대한 모든 기능 지원
    • Maven, Gradle과 같은 빌드 도구 기능 지원
    • Git, Github와 같은 VCS(버전 관리 시스템) 기능 지원
    • Spring-boot의 경우, 톰캣과 같은 별도의 외장 서버없이 실행 가능

 

 

 저는 JetBrain의 Toolbox를 사용하여 IntelliJ를 설치했습니다. Toolbox는 JetBrain의 제품 전체를 관리하고 쉽게 설치할 수 있기때문에 추천합니다! (IntelliJ의 설치 과정은 따로 첨부하지 않겠습니다.)

 

지금까지 교재의 25%를 보며 느낀 것은 2년 전, SSAFY에서 무작정 기능 개발에만 몰두했던 제 자신을 반성하게 됐습니다.

 그때 당시, Swagger로 API를 테스트하며 매번 톰캣을 재시작하는 과정을 당연하게 여겼습니다. 게다가 Mybatis로 쿼리문을 작성 및 관리하며 많은 시간을 할애했습니다. 물론, 이러한 과정들이 틀린 것만은 아닙니다.

 하지만, 다양한 기술들을 활용하여 개발 시간을 단축시키는 동시에, 유지보수가 용이한 코딩을 하는 것이 개발자의 역할이라고 생각합니다.

 

 

✅테스트 코드 작성하기

 개발자로 취업준비를 하며 TDD, 단위 테스트라는 용어를 접했다. Swagger나 Postman을 사용하며 API 테스트의 중요성은 느꼈지만, 테스트 코드? 기능 개발하느라 코딩하는 시간도 부족한데 테스트 코드까지 작성해야하나 싶었다.

 

 교재에서는 짧고 굵게 테스트 코드의 중요성을 강조했다.

  • 단위 테스트는 개발단계 초기에 문제를 발견하게 도와줍니다.
  • 단위 테스트는 개발자가 나중에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인할 수 있습니다. (예. 회귀테스트)
  • 단위 테스트는 기능에 대한 불확실성을 감소시킬 수 있습니다.
  • 단위 테스트는 시스템에 대한 실제 문서를 제공합니다. 즉, 단위 테스트 자체가 문서로 사용할 수 있습니다.

 

실제로 제가 SSAFY에서 Spring-boot기반 프로젝트를 할 때, 개발 과정은 다음과 같았습니다.

  1. 코드 작성하기
  2. 프로그램 실행
  3. Swagger와 같은 API 테스트 도구로 HTTP 요청
  4. 요청 결과를 출력문을 통해 확인
  5. 결과가 다르면 1~4번을 다시 반복

 여기서, 코드를 수정하는 상황이 생기면 2~5번 과정을 매번 반복했습니다. 실제로 코드 작성한 양은 몇 줄 안됐지만, 테스트하느라 하루를 다 날린 적도 있었습니다.

 

 이제는 테스트 코드를 작성하며 위와 같은 수고를 덜 수 있게 됐습니다! Spring-boot 프로젝트를 생성하면 항상 src/test 디렉토리가 왜 있나 궁금했는데, 이제서야 이유를 알게됐습니다.😂

(실제 교재를 작성하신 '이동욱 개발자'님의 경험담은 교재를 통해 확인해주세요!)

 

 해당 교재에서는 Java의 테스트 프레임워크인 JUnit4를 사용합니다. 아래는 Controller의 API를 테스트하는 간단한 코드입니다.

 

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void testHelloAPI throws Exception {
        String hello = "hello";

        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }
}

 

 Class명처럼 HelloController에 작성된 API를 테스트하는 코드입니다. 각 Anntotation과 코드는 아래와 같은 역할을 하고 있습니다.

 

코드 역할
@Runwith  테스트 진행 시, JUnit에 내장된 실행자 외에 다른 실행자를 실행합니다.
Spring-boot 테스트와 JUnit 사이에 연결자
@WebMvcTest Web에 집중할 수 있는 Annotation
(단, @Service, @Component, @Repository 등은 사용할 수 없습니다.)
@Autowired 스프링이 관리하는 빈(Bean)을 주입합니다.
MockMvc 웹 API를 테스트할 때 사용합니다.
mvc.perform MockMvc를 통해 특정 주소로 HTTP 요청을 합니다.
체이닝이 지원되어 여러 검증 기능이 있습니다.
.andExpect mvc.perform의 결과를 검증합니다.
HTTP Header status를 검증하거나 응답 본문의 내용을 검증하는 등의 역할을 수행합니다.

 

 

✅Lombok 사용하기

 Lombok은 자바 개발 시, 자주 사용하는 코드인 Getter, Setter, 생성자, toString 등을 Annotation으로 자동 생성해줍니다. IntelliJ의 경우, 플러그인과 build.gradle을 통해 쉽게 Lombok을 설정할 수 있습니다.

 

 간단하게 dto코드를 구현하면 아래와 같습니다.

 

public class HelloResponseDto {
    private String name;
    private int amount;
    
    HelloResonseDto() {};
    
    public String getName() {
        return this.name;
    }
    
    public int getAmount() {
        return this.amount;
    }
}

 Java로 처음 개발할 당시, 모든 dto를 이런식으로 작성했습니다. 위에서는 변수가 2개뿐이지만, 5개이상 넘어갈 경우, dto코드는 배로 늘어납니다.

 

 이제 Lombok을 적용한 코드를 보겠습니다.

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
    private final String name;
    private final int amount;
}

 

 위 코드를 보면 Class에 적용된 2개의 Annotation과 final로 선언된 변수, 단순화된 코드를 확인할 수 있습니다. 이러한 상태는 개발 초기단계에 자주 발생하는 변경사항들을 빠르게 대응할 수 있게됩니다. 코드가 짧아진 만큼 내부 동작과정이나 각 코드의 역할들을 정확히 알고 있어야합니다.

 하지만, 코드가 간결해진만큼 개발 시간을 단축시킬 수 있습니다👍

 

코드 역할
@Getter 선언된 모든 필드의 get 메소드를 생성합니다.
@RequiredArgsConstructor final이 없는 필드는 생성자에 포함되지 않습니다.

 

✅JPA로 DB를 다뤄보자

 SSAFY에서 SQL mapper인 Mybatis로 데이터베이스를 관리할 당시, Java 코드보다 쿼리문을 작성하는 날이 많았습니다. 하지만, 이러한 문제의 해결책으로 JPA라는 자바 표준 ORM을 알게 됐습니다. 여기서 ORM이란, 코드의 객체를 매핑하여 간접적으로 DB를 조작하는 역할을 수행합니다.

 

 웹/앱 개발을 하다보면 각 객체마다 항상 CRUD를 기본적으로 작성합니다. 결국, 아래와 같은 쿼리문을 피할 수 없습니다. 실제로 저도 앱 개발을 하며 각 Table마다 아래 쿼리문과 쿼리문을 호출하는 메서드를 작성했습니다.

INSERT INTO table_name(...) VALUES (...) ;
SELECT * FROM table_name WHERE ... ;
UPDATE table_name SET ... WHERE ... ;
DELETE FROM table_name WHERE ... ;

 

 해당 교재에서는 이런 단순 반복뿐만 아니라, 패러다임의 불일치라는 문제를 언급합니다. RDB는 데이터를 어떻게 저장할지에 초점이 맞춰져 있지만, OOP언어는 메세지를 기반으로 기능과 속성을 한 곳에서 관리하는 기술입니다.

 

 이렇게 서로 지향하는 바가 다른 RDB와 OOP언어를 중간에서 연결해주는 기술이 바로 JPA입니다. 즉, 개발자는 OOP기반으로 개발하면, JPA가 이를 RDB에 맞게 SQL을 대신 생성해서 실행합니다. 개발자는 항상 객체 지향적으로 코드를 표현할 수 있기 때문에, 쿼리문 작성과 DB 모델링에 대한 부담을 줄일 수 있게 됩니다.

 

 자바 표준 명세서이자 인터페이스인 JPA는 사용하려면 구현체가 필요합니다. 대표적으로 Hibernate, Eclipse, Link 등이 있지만, Spring에서는 이 구현체들을 다루지 않습니다.

 

 대신, 구현체들을 추상화시킨 Spring Data JPA라는 모듈을 이용합니다. Hibernate를 쓰는 것과 큰 차이는 없지만 2가지 이점이 있습니다.

 

  1. 구현체 교체의 용이성
  2. 저장소 교체의 용이성

 

 2가지 이점의 공통점은 프로젝트를 전환하는데 더욱 쉽고 빠르게할 수 있다는 것입니다. Spring Data JPA는 내부에서 구현체 매핑을 지원합니다. 또한, 다른 저장소를 사용할 경우, Spring Data JPA를 Spring Data MongoDB로 의존성만 교체해주면 됩니다.

 

 이렇게 강력한 이점이 있는만큼 JPA를 완벽하게 사용하려면 OOP와 RDB를 둘다 이해해야 합니다. 

 

 

아래부터는 Lombok과 JPA를 활용한 코드를 살펴보겠습니다.

@Getter
@NoArgsConstructor
@Entity
public class Posts {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 500, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
}

 

 해당 Class의 특이점은 Setter 메서드가 없다는 것입니다. (@Setter도 없습니다.)

 자바빈 규약을 살펴보면 Getter/Setter 메서드를 무조건 생성합니다. 틀린 것은 아니지만, 프로젝트 스케일이 커질 수록, 해당 Class의 인스턴스 값들이 언제 어디서 왜 변하는지 코드상으로 명확하게 인지할 수 없습니다.

 

 이러한 이유 때문에 Entity Class에서는 절대 Setter 메서드를 만들지 않습니다. 대신, 해당 필드의 값 변경이 필요하면 명확한 목적과 의도를 나타낼 수 있는 메서드를 추가해야만 합니다.

 

 예를 들어, 주문 취소 메서드를 만든다고 할 경우, setStatus() 가 아닌 cancelOrder() 메서드를 추가하는 것입니다.

 

 이외에도 기본적인 구조를 생성자를 통해 최종값을 채운 후, DB에 INSERT합니다. 다만, 생성자 대신 Builder 패턴을 활용하여 어느 필드에 어떤 값을 채워야할지 명확하게 인지합니다. 위 코드에서는 @Builder를 통해 제공되는 빌더 클래스를 사용했습니다.

 

 

🙋‍♂️느낀점

지금까지 교재의 25%를 공부하며 배우고 느낀점들을 작성하였습니다. 아직 IntelliJ와 JPA, JUnit 테스트, Gradle을 활용했지만, 전부터 보고 들었던 기술들이어서 재밌게 공부하고 있습니다. 처음 책을 폈을 때는 '내가 궁금했던 것들이잖아?!'라는 생각이 들며 설레기도 했습니다. (❁´◡`❁)

 

 해당 교재는 코드 한줄 한줄 깊은 설명은 없지만, 왜 작성하는지에 대해 알고 넘어갈 수 있습니다. 게다가 왜 이 도구를, 왜 해당 기술을 사용하는지 명확하게 알면서 공부할 수 있다는 점이 큰 매력이라고 생각합니 다. 기계적인 프로그래밍이 아닌 생각하며 코딩하는 개발자가 되고 싶은 분들께 해당 교재를 추천합니다!

 

 한 마디로 정의한다면, 깊지 않지만 충분하다.

 

 

 

728x90
반응형