스프링 부트 백엔드 개발 강좌, 테스트 코드 패턴 연습하기

스프링 부트(Spring Boot)는 스프링 프레임워크의 하위 프로젝트로, 복잡한 설정 없이 빠르게 애플리케이션을 개발할 수 있도록 도와줍니다. 본 강좌에서는 스프링 부트를 사용하여 백엔드 애플리케이션을 개발하는 과정을 다루며, 특히 테스트 코드 패턴에 중점을 두어 효과적인 테스트 코드를 작성하는 방법을 연습합니다.

1. 스프링 부트 소개

스프링 부트는 스프링 프레임워크의 복잡한 설정을 최소화하여 빠르게 애플리케이션을 빌드할 수 있는 도구입니다. 이 프레임워크는 내장된 서버, 자동 설정, 그리고 종속성 관리를 통해 개발자가 더 많은 시간과 노력을 애플리케이션 로직에 집중할 수 있게 합니다.

1.1. 스프링 부트의 특징

  • 자동 설정: 개발자가 설정해야 할 부분을 최소화합니다.
  • 의존성 관리: Maven 또는 Gradle을 통해 필요한 라이브러리를 쉽게 추가할 수 있습니다.
  • 내장 서버: Tomcat, Jetty 등 내장된 서버로 테스트와 배포가 용이합니다.
  • Production Ready: 다양한 설정이 기본적으로 준비되어 있어 운영 환경에 바로 배포 가능합니다.

2. 테스트 코드 작성의 중요성

테스트 코드는 소프트웨어 개발의 필수 요소입니다. 코드를 변경하거나 새로운 기능을 추가할 때 기존 기능이 정상적으로 작동하는지 확인할 수 있습니다. 왜 테스트 코드가 중요한지에 대해 알아봅니다.

2.1. 코드 품질 향상

테스트 코드는 버그를 조기에 발견하고 코드의 품질을 유지하는 데 큰 도움이 됩니다. 구현한 기능이 의도한 대로 동작하는지 확인할 수 있습니다.

2.2. 리팩토링의 용이성

코드 리팩토링 시, 테스트 코드가 있다면 변경 사항이 기존 기능에 미치는 영향을 손쉽게 확인할 수 있어 안정적인 리팩토링이 가능합니다.

3. 스프링 부트에서의 테스트 코드 작성

스프링 부트에서는 JUnit과 Mockito 같은 테스트 도구를 활용하여 쉽게 테스트 코드를 작성할 수 있습니다. 여기서는 유닛 테스트, 통합 테스트, 및 Mock 테스트를 다룰 것입니다.

3.1. 유닛 테스트

유닛 테스트는 애플리케이션의 개별 구성 요소를 테스트하는 것을 말합니다. JUnit은 자바에서 유닛 테스트를 작성할 때 널리 사용되는 프레임워크입니다.

import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;

class UserServiceTest {
    private UserService userService = new UserService();
    
    @Test
    void testAddUser() {
        User user = new User("testUser");
        userService.addUser(user);
        verify(userRepository).save(user);
    }
}

3.2. 통합 테스트

통합 테스트는 여러 구성 요소 간의 상호작용을 테스트합니다. 스프링 부트에서는 @SpringBootTest 애너테이션을 사용하여 통합 테스트를 수행할 수 있습니다.

import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;

@SpringBootTest
class UserServiceIntegrationTest {

    @Test
    void testGetUser() {
        User user = userService.getUserById(1L);
        assertNotNull(user);
        assertEquals("testUser", user.getName());
    }
}

3.3. Mock 테스트

Mock 객체를 사용하면 외부 의존성을 제거한 테스트를 작성할 수 있습니다. Mockito를 활용하여 Mock 객체를 생성하고 원하는 동작을 지정할 수 있습니다.

import static org.mockito.Mockito.*;

class UserService {
    private UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

// 테스트 클래스
class UserServiceMockTest {
    private UserRepository userRepository = mock(UserRepository.class);
    private UserService userService = new UserService(userRepository);
    
    @Test
    void testGetUserReturnsUserWhenExists() {
        User user = new User("testUser");
        when(userRepository.findById(anyLong())).thenReturn(Optional.of(user));
        
        User foundUser = userService.getUserById(1L);
        assertNotNull(foundUser);
        assertEquals("testUser", foundUser.getName());
    }
}

4. 테스트 코드 패턴

테스트 코드의 작성 방식에는 여러 가지 패턴이 있습니다. 이 패턴을 이해하고 활용함으로써 더 나은 품질의 테스트 코드를 작성할 수 있습니다.

4.1. AAA 패턴

AAA 패턴은 Arrange-Act-Assert로 구성되어 있습니다. 이 패턴은 테스트의 구조를 명확하게 구분하여 가독성을 높입니다.

void testAddUser() {
        // Arrange
        User user = new User("testUser");
        
        // Act
        userService.addUser(user);
        
        // Assert
        verify(userRepository).save(user);
    }

4.2. Given-When-Then 패턴

Given-When-Then 패턴은 시나리오 기반의 테스트를 작성할 때 유용합니다. 각 단계가 명확하게 구분되어 있어 이해하기 쉽습니다.

void testAddUser() {
        // Given
        User user = new User("testUser");
        
        // When
        userService.addUser(user);
        
        // Then
        verify(userRepository).save(user);
    }

5. 스프링 부트 테스트 환경 설정

스프링 부트에서 테스트 환경을 설정하는 방법과 필요한 의존성에 대해 설명합니다. Maven을 사용하여 테스트 의존성을 추가하는 예를 보여드리겠습니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

6. 결론

본 강좌에서는 스프링 부트를 활용한 백엔드 개발과 테스트 코드 작성에 대해 알아보았습니다. 테스트는 소프트웨어 개발의 중요한 부분이며, 제대로 된 테스트 코드를 작성함으로써 애플리케이션의 안정성과 품질을 높일 수 있습니다. 다양한 테스트 패턴과 도구를 활용하여 더 나은 소프트웨어를 개발하시길 바랍니다.

7. 참고 문헌

스프링 부트 백엔드 개발 강좌, 클라이언트란

현대 웹 애플리케이션의 구조는 클라이언트와 서버로 크게 나눌 수 있습니다. 클라이언트는 사용자와의 직접적인 인터페이스를 담당하고, 서버는 비즈니스 로직과 데이터 관리를 수행합니다. 본 강좌에서는 클라이언트의 기능과 역할, 그리고 스프링 부트를 이용한 백엔드 개발에서 클라이언트와의 상호작용에 대해 자세히 알아보겠습니다.

1. 클라이언트의 정의

클라이언트란 사용자 인터페이스를 제공하는 시스템을 의미합니다. 일반적으로 웹 브라우저와 같은 응용 프로그램에서 서버에 요청을 보내어 데이터를 받고, 이를 사용자에게 보여주는 역할을 합니다. 클라이언트는 다음과 같은 여러 유형으로 나눌 수 있습니다:

  • 웹 클라이언트: 웹 브라우저를 통해 HTML, CSS 및 JavaScript를 사용하여 사용자에게 웹 페이지를 제공합니다.
  • 모바일 클라이언트: 스마트폰이나 태블릿에서 실행되는 애플리케이션으로, 네이티브 앱 또는 하이브리드 앱 형태로 존재합니다.
  • 데스크탑 클라이언트: 특정 운영체제에서 실행되는 데스크탑 애플리케이션으로, 웹과 상호작용할 수 있습니다.

2. 클라이언트의 역할

클라이언트의 주요 역할은 다음과 같습니다:

  • 사용자 인터페이스 제공: 사용자가 데이터에 접근하고 조작할 수 있는 그래픽 환경을 제공합니다.
  • 데이터 요청: 서버에 API 요청을 보내 필요한 데이터를 가져옵니다.
  • 응답 처리: 서버로부터 받은 데이터를 사용자의 화면에 표시합니다.
  • 유효성 검사: 사용자 입력에 대한 유효성 검사를 수행하여 서버로 전송하기 전에 데이터를 검증합니다.

3. 스프링 부트와 클라이언트 간의 상호작용

스프링 부트는 RESTful API를 통해 클라이언트와 상호작용할 수 있는 백엔드 애플리케이션을 쉽게 개발할 수 있도록 도와줍니다. RESTful API는 HTTP 요청을 통해 클라이언트와 서버 사이의 데이터를 효율적으로 전송하는 방식입니다. 클라이언트는 스프링 부트 서버에 요청을 보내고, 서버는 JSON 형식으로 응답을 반환합니다.

3.1 REST API의 구조

REST API는 기본적으로 다음과 같은 구조를 가집니다:

  • HTTP 메서드: GET, POST, PUT, DELETE 등의 메서드를 통해 특정 작업을 수행합니다.
  • URI: 리소스를 나타내는 고유한 경로로, 각 API 엔드포인트는 특정 리소스에 매핑됩니다.
  • HTTP 상태 코드: 요청의 결과를 나타내는 숫자 코드로, 성공, 실패 등의 상태를 알려줍니다.

3.2 스프링 부트에서 REST API 구현하기

스프링 부트를 사용하여 REST API를 구현하는 과정은 다음과 같습니다:

  1. 스프링 부트 프로젝트 생성: Spring Initializr를 사용하여 필요한 종속성을 가진 프로젝트를 생성합니다.
  2. 모델 클래스 정의: 데이터베이스와 매핑할 Entity 클래스를 생성합니다.
  3. 레포지토리 인터페이스 생성: CRUD 작업을 위한 JPA 리포지토리 인터페이스를 정의합니다.
  4. 서비스 클래스 작성: 비즈니스 로직을 구현하는 서비스 클래스를 작성합니다.
  5. 컨트롤러 구현: 클라이언트의 요청을 처리하는 REST 컨트롤러 클래스를 작성합니다.

4. 클라이언트의 데이터 처리

클라이언트가 서버로부터 데이터를 수신하고 처리하는 방법에 대해 설명합니다. 클라이언트는 일반적으로 AJAX 요청이나 Fetch API를 사용하여 서버에 비동기식 요청을 전송합니다. 요청이 완료되면, 클라이언트는 응답 데이터를 가공하여 사용자에게 표시합니다.

4.1 Fetch API 사용하기

fetch('https://api.example.com/data')
        .then(response => response.json())
        .then(data => console.log(data))
        .catch(error => console.error('Error:', error));

5. 클라이언트와 서버 간의 협력

클라이언트와 서버의 협력은 원활한 사용자 경험을 보장하는 데 필수적입니다. 클라이언트는 서버에 적절한 요청을 하고, 서버는 클라이언트의 요청에 대한 응답을지속적으로 제공합니다. 이 과정에서 API 문서는 이해가 쉬워야 하며, 데이터 형식과 프로토콜이 일관성이 있어야 합니다.

6. 클라이언트의 발전 방향

클라이언트의 역할은 앞으로 더욱 중요해질 것입니다. 다양한 디바이스와 사용자의 요구에 맞춘 인터페이스 및 경험을 제공하기 위해 클라이언트 측 기술도 지속적으로 발전하고 있습니다. ახალი 프레임워크와 라이브러리들이 등장하고 있으며, SPA(Single Page Application)처럼 사용자 경험을 극대화할 수 있는 방법들은 클라이언트 기술의 핵심이 될 것입니다.

결론

클라이언트는 스프링 부트 백엔드 개발에서 매우 중요한 역할을 합니다. 클라이언트와 서버 간의 원활한 협력을 통해 사용자에게 최상의 경험을 제공할 수 있습니다. 이 강좌를 통해 클라이언트와 서버 간의 관계를 이해하고, 스프링 부트를 이용한 백엔드 개발의 기초를 익히는 데 도움이 되었으면 합니다.

스프링 부트 백엔드 개발 강좌, 테스트 컨트롤러 살펴보기

스프링 부트는 최근의 웹 애플리케이션 개발에서 가장 인기 있는 프레임워크 중 하나로, 개발자가 신속하게 프로덕션 준비가 완료된 애플리케이션을 만들 수 있도록 돕습니다. 본 강좌에서는 스프링 부트에서의 테스트 컨트롤러에 대해 자세히 살펴보겠습니다. 테스트 컨트롤러는 애플리케이션의 비즈니스 로직과 데이터 레이어 간의 상호 작용을 검증하는 중요한 부분으로, 올바른 동작을 보장하는 데 필수적입니다.

1. 스프링 부트 및 테스트 개념 이해하기

테스트는 소프트웨어 개발 주기에서 필수적인 단계로, 상대적으로 높은 품질의 코드를 유지하는데 기여합니다. 스프링 부트는 모든 기능을 테스트하는 데 필요한 모듈을 제공합니다. 단위 테스트, 통합 테스트, 그리고 E2E(End-to-End) 테스트로 나눌 수 있으며, 이 모든 테스트는 애플리케이션의 신뢰성을 높이는 데 도움을 줍니다.

1.1 단위 테스트와 통합 테스트

단위 테스트는 개별적으로 가장 작은 코드 단위(주로 메소드 단위)가 의도한 대로 동작하는지를 검증합니다. 반면, 통합 테스트는 여러 컴포넌트가 결합되어 올바르게 작동하는지를 확인합니다. 특히 스프링 부트에서는 이러한 테스트를 쉽게 구현할 수 있는 여러 도구와 프레임워크가 제공됩니다.

1.2 E2E 테스트

E2E 테스팅은 사용자의 관점에서 애플리케이션이 정상적으로 동작하는지를 확인하는 과정입니다. 통합 테스트보다 더 높은 수준의 테스트이며, 사용자가 애플리케이션을 사용하면서 겪는 전체적인 경험을 검사합니다.

2. 스프링 부트의 테스트 지원

스프링 부트는 JUnit, Mockito, Spring Test와 같은 광범위한 테스트 도구를 지원합니다. JUnit은 자바 테스트 프레임워크로, 스프링 부트 애플리케이션과 함께 사용할 수 있는 강력한 기능을 제공합니다. Mockito는 Mock 객체를 생성하여 의존성을 주입하고, 테스트할 객체의 동작을 시뮬레이션할 수 있게 해줍니다.

2.1 테스트 어노테이션

스프링 부트에서는 여러 테스트 어노테이션을 제공하여 테스트 클래스를 쉽게 구성할 수 있습니다. 여기서는 주요 어노테이션을 소개합니다:

  • @SpringBootTest: 전체 컨텍스트를 로드하여 통합 테스트를 수행하는 데 사용됩니다.
  • @WebMvcTest: MVC 컴포넌트만 로드하여 컨트롤러 테스트에 유용합니다.
  • @MockBean: 특정 빈을 Mock 객체로 대체할 때 사용됩니다.

3. 테스트 컨트롤러 구현하기

이제 테스트 컨트롤러를 구현해보도록 하겠습니다. 간단한 CRUD 애플리케이션을 예로 들겠습니다. 이 애플리케이션은 사용자 정보를 관리하는 기능을 가지고 있습니다.

3.1 의존성 주입 설정하기

 
    // build.gradle
    dependencies {
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
    

3.2 사용자 모델 및 레포지토리 구현


// User.java
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;

    // getters and setters
}

// UserRepository.java
public interface UserRepository extends JpaRepository {
}
    

3.3 사용자 컨트롤러 구현


// UserController.java
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping
    public List getAllUsers() {
        return userRepository.findAll();
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userRepository.save(user);
    }
}
    

4. 사용자 컨트롤러 테스트 적용하기

이제 컨트롤러에 대한 단위 테스트를 작성하겠습니다. @WebMvcTest 어노테이션을 사용하여 컨트롤러와 그 의존성을 테스트할 수 있습니다.


// UserControllerTest.java
@WebMvcTest(UserController.class)
public class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserRepository userRepository;

    @Test
    public void testGetAllUsers() throws Exception {
        User user = new User();
        user.setId(1L);
        user.setName("John Doe");
        user.setEmail("john@example.com");

        List users = Arrays.asList(user);

        given(userRepository.findAll()).willReturn(users);

        mockMvc.perform(get("/api/users"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.length()").value(1))
            .andExpect(jsonPath("$[0].name").value("John Doe"));
    }

    @Test
    public void testCreateUser() throws Exception {
        User user = new User();
        user.setName("Jane Doe");
        user.setEmail("jane@example.com");

        String jsonRequest = new ObjectMapper().writeValueAsString(user);

        given(userRepository.save(any(User.class))).willReturn(user);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonRequest))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.name").value("Jane Doe"));
    }
}
    

5. 테스트 실행하기

테스트는 IDE에서 직접 실행할 수도 있고, Maven 또는 Gradle을 통해 커맨드라인에서 실행할 수도 있습니다. IDE에서는 테스트 클래스 이름 우클릭 후 “Run” 버튼을 클릭하여 실행할 수 있습니다.

6. 결론

본 강좌에서는 스프링 부트에서 테스트 컨트롤러를 감사하는 방법에 대한 기본 이해를 제공합니다. 유닛 테스트와 통합 테스트는 모두 애플리케이션의 품질 보증에 필수적입니다. 컨트롤러 테스트를 통해 API의 응답을 검증하고, 신뢰성 있는 애플리케이션을 개발하는 데 도움을 줍니다. 이러한 테스트 주도 개발(TDD) 방식은 코드의 유지보수성을 높이고, 향후 발생할 수 있는 버그를 사전에 방지하는 데 큰 도움이 됩니다.

앞으로도 계속해서 스프링 부트와 관련된 다양한 주제를 다루고, 깊이 있는 내용을 제공합니다. 감사합니다. 😊

스프링 부트 백엔드 개발 강좌, 첫 번째 스프링 부트 3 예제 만들기

안녕하세요! 이번 블로그 포스트에서는 스프링 부트 3를 이용하여 첫 번째 웹 애플리케이션을 개발해보겠습니다. 스프링 부트는 자바 기반의 웹 애플리케이션을 쉽게 만들 수 있도록 도와주는 프레임워크입니다. 효율성과 생산성을 높이기 위해 설계되었으며, 많은 개발자들이 애용하고 있습니다. 이번 과정을 통해 간단한 RESTful API를 구축해보겠습니다.

1. 스프링 부트란?

스프링 부트는 스프링 프레임워크 위에 구축된 라이브러리로, 빠르고 손쉽게 스프링 애플리케이션을 만들 수 있는 환경을 제공합니다. 기본적으로 스프링 부트는 다음과 같은 장점을 가지고 있습니다:

  • 자동 설정: 스프링 부트는 애플리케이션의 설정을 자동으로 처리합니다. 이를 통해 개발자는 복잡한 설정 과정을 줄일 수 있습니다.
  • 의존성 관리: Maven 또는 Gradle을 통해 필요한 라이브러리를 쉽게 추가하고 관리할 수 있습니다.
  • Standalone: 스프링 부트 애플리케이션은 자체적으로 실행할 수 있는 JAR 파일로 빌드됩니다. 별도의 웹 서버를 사용할 필요가 없습니다.
  • 내장된 서버 지원: Tomcat, Jetty, Undertow와 같은 내장 서버를 지원하여 빠르게 테스트하고 배포할 수 있습니다.

2. 개발 환경 준비하기

스프링 부트 애플리케이션을 개발하기 위해 아래의 환경이 필요합니다:

  • Java JDK 17 이상: 스프링 부트 3.x는 Java 17 이상에서 실행됩니다. JDK를 설치 후 환경 변수를 설정해야 합니다.
  • IDE: IntelliJ IDEA, Eclipse와 같은 통합 개발 환경(IDE)을 추천합니다. 이번 예제에서는 IntelliJ IDEA를 사용합니다.
  • Build Tool: Maven 또는 Gradle 중 하나를 선택하여 의존성을 관리해야 합니다. 이번 예제에서는 Maven을 사용할 것입니다.

3. 프로젝트 생성

스프링 부트 프로젝트를 생성하는 방법은 여러 가지가 있지만, 시간 절약을 위해 Spring Initializr를 이용하여 프로젝트를 생성하겠습니다. 다음 단계를 따라 진행하세요:

  1. 웹 브라우저에서 Spring Initializr를 엽니다.
  2. 다음과 같은 설정을 입력합니다:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 3.x.x (가장 최신 버전)
    • Project Metadata:
      • Group: com.example
      • Artifact: demo
      • Name: demo
      • Description: Demo project for Spring Boot
      • Package name: com.example.demo
      • Packaging: Jar
      • Java: 17
  3. Dependencies에서 다음을 추가합니다:
    • Spring Web
    • Spring Data JPA
    • H2 Database
  4. Generate 버튼을 클릭하여 ZIP 파일을 다운로드한 후, 이를 압축 해제합니다.

4. IDE에서 프로젝트 열기

IntelliJ IDEA를 실행한 후, 다음과 같이 프로젝트를 엽니다:

  1. File 메뉴를 클릭하고 “Open”을 선택합니다.
  2. 압축 해제한 프로젝트 폴더를 선택한 후 열기 버튼을 클릭합니다.
  3. Gradle 또는 Maven을 선택하여 필요한 라이브러리를 다운로드합니다.

5. 기본 애플리케이션 코드 작성하기

프로젝트의 기본 구조가 생성된 후, 애플리케이션의 엔트리 포인트인 DemoApplication.java 파일이 생성됩니다. 해당 파일은 다음과 같이 생겼습니다:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

이제 REST API를 만들기 위해 컨트롤러 클래스를 추가하겠습니다. src/main/java/com/example/demo 경로에 HelloController.java라는 파일을 생성합니다.

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot 3!";
    }
}

위의 코드는 “/hello” URL로 GET 요청을 보낼 경우 “Hello, Spring Boot 3!”라는 문자열을 반환하는 간단한 컨트롤러입니다.

6. 애플리케이션 실행하기

이제 애플리케이션을 실행해 봅시다. IntelliJ IDEA의 상단 메뉴에서 아래와 같이 실행 버튼을 클릭합니다:

  • Run → Run ‘DemoApplication’

애플리케이션이 성공적으로 시작되면, 콘솔에 “Started DemoApplication in …” 메시지가 출력됩니다. 이제 웹 브라우저를 열고 http://localhost:8080/hello URL에 접속해보세요.

정상적으로 작동하면 “Hello, Spring Boot 3!” 메시지를 확인할 수 있습니다.

7. H2 데이터베이스를 이용한 간단한 CRUD 구현하기

이제 H2 데이터를 이용하여 간단한 CRUD Create, Read, Update, Delete 작업을 구현해보겠습니다. 먼저, src/main/resources/application.properties 파일에 다음과 같은 내용을 추가하여 H2 데이터베이스를 설정합니다:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create

이제 모델 클래스를 생성합니다. src/main/java/com/example/demo 경로에 Product.java 파일을 생성합니다.

package com.example.demo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

이제, Product 모델에 대한 CRUD 작업을 수행할 수 있는 서비스와 컨트롤러를 만들어보겠습니다. src/main/java/com/example/demo 경로에 ProductController.java 파일을 생성합니다.

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    @GetMapping
    public List getAllProducts() {
        return productRepository.findAll();
    }

    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        return productRepository.save(product);
    }

    @GetMapping("/{id}")
    public ResponseEntity getProductById(@PathVariable Long id) {
        return productRepository.findById(id)
                .map(product -> ResponseEntity.ok().body(product))
                .orElse(ResponseEntity.notFound().build());
    }

    @PutMapping("/{id}")
    public ResponseEntity updateProduct(@PathVariable Long id, @RequestBody Product productDetails) {
        return productRepository.findById(id)
                .map(product -> {
                    product.setName(productDetails.getName());
                    product.setPrice(productDetails.getPrice());
                    Product updatedProduct = productRepository.save(product);
                    return ResponseEntity.ok().body(updatedProduct);
                }).orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity deleteProduct(@PathVariable Long id) {
        return productRepository.findById(id)
                .map(product -> {
                    productRepository.delete(product);
                    return ResponseEntity.noContent().build();
                }).orElse(ResponseEntity.notFound().build());
    }
}

마지막으로, ProductRepository 인터페이스를 생성하여 데이터베이스와 상호작용할 수 있는 메소드를 정의합니다. src/main/java/com/example/demo 경로에 ProductRepository.java 파일을 생성합니다.

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository {
}

8. 애플리케이션 실행 및 테스트하기

애플리케이션을 다시 실행하고, Postman과 같은 API 테스트 도구를 사용하여 CRUD API를 테스트해봅시다. 다음은 각 요청에 대한 예시입니다:

  • Create Product:
  •     POST /products
        {
            "name": "Sample Product",
            "price": 19.99
        }
        
  • Get All Products:
  •     GET /products
        
  • Get Product by ID:
  •     GET /products/{id}
        
  • Update Product:
  •     PUT /products/{id}
        {
            "name": "Updated Product",
            "price": 29.99
        }
        
  • Delete Product:
  •     DELETE /products/{id}
        

9. H2 콘솔 확인하기

H2 데이터베이스를 통해 데이터 관리도 가능합니다. 웹 브라우저를 열고 http://localhost:8080/h2-console에 접속해보세요. JDBC URL에 jdbc:h2:mem:testdb를 입력하고, 사용자 이름에 sa를 입력한 후 Connect 버튼을 클릭합니다.

이제 H2 데이터베이스에서 데이터를 조회하거나 삭제하는 등 다양한 작업을 수행할 수 있습니다.

10. 마무리 및 다음 단계

이번 포스트에서는 스프링 부트 3를 사용하여 간단한 RESTful API와 H2 데이터베이스를 통해 CRUD 기능을 구현해보았습니다. 이 예제를 통해 스프링 부트의 기본 개념과 작동 방식을 이해할 수 있었길 바랍니다. 이후에는 더 복잡한 데이터베이스와 비즈니스 로직을 추가하여 애플리케이션을 발전시킬 수 있습니다.

다음 블로그 포스트에서는 인증과 인가를 위한 Spring Security에 대해 다뤄볼 예정입니다. 감사합니다!

스프링 부트 백엔드 개발 강좌, 카페와 빵집으로 이해하는 계층

오늘날의 소프트웨어 개발에서 백엔드 시스템은 사용자가 웹 애플리케이션이나 모바일 애플리케이션을 사용할 때 뒤에서 silently 작동하며 데이터 처리를 담당합니다. 이 강좌에서는 스프링 부트를 사용하여 백엔드를 개발하는 방법을 배우고, 이를 카페와 빵집의 비유를 통해 계층 구조를 이해해볼 것입니다.

1. 스프링 부트란?

스프링 부트(Sprint Boot)는 스프링 프레임워크를 기반으로 한 Java 기반의 프레임워크로, 복잡한 설정 없이 애플리케이션을 신속하게 구축할 수 있도록 돕습니다. 스프링 부트는 기능을 추가하기 위한 다양한 스타터 패키지와 기본적으로 제공하는 설정으로, 사용자 정의에 필요한 수고를 덜어줍니다.

2. 백엔드 개발 개요

백엔드 개발은 데이터 저장, 비즈니스 로직 처리, API 제공 등 다양한 작업을 포함합니다. 배달 음식 주문 웹사이트를 예로 들면, 사용자는 프론트엔드에서 메뉴 선택과 결제를 진행하며, 백엔드는 이를 처리하여 주문 내역을 저장하고 주방에 전달합니다.

3. 계층적 아키텍처 이해하기

계층적 아키텍처는 소프트웨어 설계의 일반적인 패턴으로, 각 레이어가 특정 역할을 수행합니다. 우리 예시로 ‘카페와 빵집’ 시스템을 생각해 보겠습니다.

  • 프레젠테이션 레이어: 사용자가 주문을 하기 위해 카페에 들어옵니다. 이는 웹 브라우저에서 표현되는 UI입니다.
  • 비즈니스 레이어: 바리스타가 고객의 요구에 따라 주문을 처리합니다. 이는 비즈니스 로직을 담당하는 서비스 계층입니다.
  • 데이터 레이어: 고객의 주문 정보가 저장되는 곳입니다. 서버의 데이터베이스에 영구적으로 저장됩니다.

4. 스프링 부트 프로젝트 생성하기

스프링 부트 프로젝트는 Spring Initializr를 통해 쉽게 생성할 수 있습니다. 다음은 Gradle을 사용하는 예시입니다.

curl https://start.spring.io/starter.zip \
    -d dependencies=web,jpa,mysql \
    -d name=cafe-bakery \
    -d packageName=com.example.cafe \
    -o cafe-bakery.zip

5. 프레젠테이션 레이어 구축

프레젠테이션 레이어는 웹 브라우저에서 요청된 내용을 처리합니다. Spring MVC를 사용하여 RESTful API를 구현할 수 있습니다.

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @Autowired
    private OrderService orderService;

    @PostMapping
    public ResponseEntity createOrder(@RequestBody Order order) {
        Order createdOrder = orderService.createOrder(order);
        return new ResponseEntity<>(createdOrder, HttpStatus.CREATED);
    }
}

6. 비즈니스 로직 구현하기

비즈니스 레이어는 실제 비즈니스 로직을 담당합니다. 다음은 주문을 생성하는 서비스 레이어의 예시입니다.

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;

    public Order createOrder(Order order) {
        // 비즈니스 로직
        return orderRepository.save(order);
    }
}

7. 데이터 레이어 구축

데이터 레이어는 데이터베이스와 상호 작용합니다. 다음은 주문을 처리할 수 있는 JPA 레포지토리입니다.

@Repository
public interface OrderRepository extends JpaRepository {
}

8. MySQL 데이터베이스 설정

스프링 부트와 MySQL 데이터베이스를 연결하기 위해 application.properties 파일을 설정해야 합니다.

spring.datasource.url=jdbc:mysql://localhost:3306/cafe_bakery
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update

9. 테스트와 배포

스프링 부트 애플리케이션은 JUnit과 Mockito를 사용하여 테스트할 수 있습니다. 각각의 계층을 테스트함으로써 코드가 올바르게 작동하는지 확인할 수 있습니다.

@SpringBootTest
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @MockBean
    private OrderRepository orderRepository;

    @Test
    public void createOrder_ShouldReturnOrder_WhenOrderIsValid() {
        Order order = new Order(...);
        when(orderRepository.save(any())).thenReturn(order); // Mock behavior
        Order createdOrder = orderService.createOrder(order);
        assertNotNull(createdOrder);
    }
}

10. 결론

이번 강좌에서는 스프링 부트를 통한 백엔드 개발의 기본적인 흐름과 계층적 아키텍처를 카페와 빵집의 비유로 설명했습니다. 각 레이어가 어떻게 협력하여 전체 시스템이 작동하는지 이해하는 것이 중요합니다. 이제 여러분은 간단한 백엔드 시스템을 구축하고 운영할 준비가 되어 있습니다.

11. 추가 자료