IT 강좌(IT Lectures)/Java

16강. 실전 프로젝트

소울입니다 2024. 7. 3. 07:58
728x90
반응형

챕터 16: 실전 프로젝트


이 장에서는 실전 프로젝트를 통해 웹 애플리케이션 개발, 마이크로서비스 아키텍처, 배포 및 유지보수 전략을 다룹니다. 각 주제를 실습 예제와 함께 상세히 설명하여 실무에서 활용할 수 있도록 합니다.

16.1 웹 애플리케이션 개발

16.1.1 프론트엔드와 백엔드 통합

프론트엔드와 백엔드의 통합은 현대 웹 애플리케이션 개발의 핵심입니다. 백엔드는 데이터 처리 및 비즈니스 로직을 담당하고, 프론트엔드는 사용자와의 상호작용을 담당합니다.

 

예제: 프론트엔드와 백엔드 통합

  1. 백엔드(Spring Boot) 설정

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>
 

application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydatabase
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
 
  1. 백엔드 REST API 구현

UserController.java

import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

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

    @PutMapping("/{id}")
    public User updateUser(@PathVariable int id, @RequestBody User user) {
        return userService.updateUser(id, user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable int id) {
        userService.deleteUser(id);
    }
}
 

설명:

  • @RestController: RESTful 웹 서비스를 처리하는 컨트롤러 클래스에 사용됩니다.
  • @RequestMapping: 요청 URL을 매핑합니다.
  • @GetMapping, @PostMapping, @PutMapping, @DeleteMapping: HTTP 메서드와 매핑되는 요청을 처리합니다.
  • UserService: 사용자 관리 로직을 처리하는 서비스 클래스입니다.
  1. 프론트엔드 (React) 설정

package.json

{
  "name": "frontend",
  "version": "1.0.0",
  "dependencies": {
    "axios": "^0.21.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  }
}
j
  1. 프론트엔드 React 컴포넌트

App.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';

const App = () => {
  const [users, setUsers] = useState([]);
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  useEffect(() => {
    fetchUsers();
  }, []);

  const fetchUsers = async () => {
    const response = await axios.get('/api/users');
    setUsers(response.data);
  };

  const createUser = async () => {
    const response = await axios.post('/api/users', { name, email });
    setUsers([...users, response.data]);
    setName('');
    setEmail('');
  };

  return (
    <div>
      <h1>User Management</h1>
      <div>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Name"
        />
        <input
          type="text"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Email"
        />
        <button onClick={createUser}>Create User</button>
      </div>
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            {user.name} ({user.email})
          </li>
        ))}
      </ul>
    </div>
  );
};

export default App;
 

설명:

  • useState: 컴포넌트 상태를 관리하는 React 훅입니다.
  • useEffect: 컴포넌트가 마운트될 때 실행되는 훅입니다.
  • axios: HTTP 요청을 보내기 위한 라이브러리입니다.
  • fetchUsers: 서버로부터 사용자 목록을 가져오는 함수입니다.
  • createUser: 새로운 사용자를 생성하는 함수입니다.

16.1.2 사용자 인증 및 권한 관리

사용자 인증 및 권한 관리는 웹 애플리케이션의 보안을 강화하는 중요한 요소입니다.

예제: Spring Security를 이용한 사용자 인증

  1. Spring Security 의존성 추가

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>
 
  1. 보안 설정

SecurityConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/users/**").authenticated()
                .anyRequest().permitAll()
            .and()
            .httpBasic();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
 

설명:

  • @EnableWebSecurity: Spring Security 설정을 활성화합니다.
  • configure: HTTP 보안 설정을 구성합니다.
  • authorizeRequests: 요청 URL에 대한 접근 권한을 설정합니다.
  • httpBasic: HTTP Basic 인증을 사용합니다.
  • PasswordEncoder: 비밀번호 인코딩을 위한 빈을 정의합니다.

추가 예제: 사용자 인증 및 권한 관리

UserDetailsServiceImpl.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collections;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 사용자 이름으로 사용자 조회
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        // UserDetails 객체 반환
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), Collections.emptyList());
    }
}
 

설명:

  • UserDetailsService: Spring Security에서 사용자 인증 정보를 제공하는 인터페이스입니다.
  • loadUserByUsername: 사용자 이름을 기반으로 사용자 정보를 로드합니다.
  • UsernameNotFoundException: 사용자를 찾지 못했을 때 발생하는 예외입니다.

16.2 마이크로서비스 아키텍처

마이크로서비스 아키텍처는 애플리케이션을 여러 개의 작은 서비스로 나누어 개발, 배포, 확장을 용이하게 하는 아키텍처입니다.

 

배경과 역사

마이크로서비스 아키텍처는 2010년대 초반에 등장하여 빠르게 인기를 얻었습니다. 이 아키텍처는 애플리케이션의 복잡성을 줄이고, 독립적으로 배포 가능한 작은 단위로 나누어 개발 효율성을 높입니다.

 

16.2.1 마이크로서비스의 개념과 장점

마이크로서비스는 다음과 같은 장점을 제공합니다:

  • 독립적인 배포: 각 서비스는 독립적으로 배포되고 관리될 수 있습니다.
  • 확장성: 서비스 단위로 확장이 가능하며, 필요한 서비스만 확장할 수 있습니다.
  • 유연한 기술 선택: 각 서비스는 독립적으로 개발될 수 있어, 적절한 기술 스택을 선택할 수 있습니다.
  • 향상된 유지보수성: 코드베이스가 작아 유지보수가 용이합니다.

16.2.2 Spring Cloud를 이용한 마이크로서비스 구현

Spring Cloud는 마이크로서비스 아키텍처를 구현하는 데 도움을 주는 다양한 툴과 라이브러리를 제공합니다.

예제: Spring Cloud를 이용한 간단한 마이크로서비스

  1. Eureka Server 설정

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>
 

application.yml

server:
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
  server:
    enable-self-preservation: false
 

EurekaServerApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

설명:

  • @EnableEurekaServer: Eureka 서버를 활성화합니다.
  • application.yml: Eureka 서버 설정을 정의합니다.
  1. 마이크로서비스 설정

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>
 

application.yml

server:
  port: 8081

spring:
  application:
    name: user-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
 

UserServiceApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

UserController.java

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

@RestController
public class UserController {

    @GetMapping("/users")
    public String getUsers() {
        return "User List";
    }
}
 

설명:

  • @EnableEurekaClient: Eureka 클라이언트를 활성화합니다.
  • application.yml: 서비스가 Eureka 서버에 등록될 수 있도록 설정합니다.
  • UserController: 사용자 목록을 반환하는 간단한 컨트롤러입니다.

16.3 배포와 유지보수 전략

애플리케이션의 배포와 유지보수는 소프트웨어 개발의 중요한 부분입니다. CI/CD 파이프라인을 구축하여 자동화된 배포를 구현하고, 모니터링과 로깅을 통해 애플리케이션의 상태를 지속적으로 관리할 수 있습니다.

 

16.3.1 CI/CD 파이프라인 구축

CI/CD(Continuous Integration/Continuous Deployment)는 소프트웨어 개발의 효율성을 높이고, 배포 과정을 자동화하는 방법입니다.

예제: Jenkins를 이용한 CI/CD 파이프라인 구축

  1. Jenkins 설치 및 설정
    • Jenkins는 오픈 소스 자동화 서버로, CI/CD 파이프라인을 구축하는 데 사용됩니다.
    • Jenkins 설치 후 기본 설정을 완료합니다.
  2. Jenkins Pipeline 구성

Jenkinsfile

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building...'
                sh './mvnw clean package'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing...'
                sh './mvnw test'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
                // 배포 스크립트 실행
                sh './deploy.sh'
            }
        }
    }
}
 

deploy.sh

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building...'
                sh './mvnw clean package'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing...'
                sh './mvnw test'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
                // 배포 스크립트 실행
                sh './deploy.sh'
            }
        }
    }
}
 

remote-deploy.sh

#!/bin/bash
echo "Starting application..."
# 애플리케이션 실행
java -jar /path/to/deploy/*.jar &

 

설명:

  • pipeline: Jenkins 파이프라인을 정의합니다.
  • stages: 빌드, 테스트, 배포 단계로 구성됩니다.
  • sh: 쉘 명령어를 실행합니다.
  • deploy.sh: 배포 스크립트를 실행합니다.
    • scp: 빌드된 JAR 파일을 원격 서버에 복사합니다.
    • ssh: 원격 서버에서 remote-deploy.sh 스크립트를 실행합니다.
  • remote-deploy.sh: 원격 서버에서 애플리케이션을 실행합니다.

추가 예제: Docker를 이용한 CI/CD 파이프라인 구축

Jenkinsfile

pipeline {
    agent any

    environment {
        DOCKER_CREDENTIALS_ID = 'dockerhub-credentials'
        DOCKER_IMAGE = 'your-docker-image'
    }

    stages {
        stage('Build') {
            steps {
                echo 'Building...'
                sh './mvnw clean package' // 프로젝트 빌드
            }
        }
        stage('Test') {
            steps {
                echo 'Testing...'
                sh './mvnw test' // 테스트 실행
            }
        }
        stage('Docker Build') {
            steps {
                echo 'Building Docker image...'
                script {
                    docker.build(DOCKER_IMAGE)
                }
            }
        }
        stage('Docker Push') {
            steps {
                echo 'Pushing Docker image...'
                script {
                    docker.withRegistry('', DOCKER_CREDENTIALS_ID) {
                        docker.image(DOCKER_IMAGE).push()
                    }
                }
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
                // 배포 스크립트 실행
                sh './deploy.sh'
            }
        }
    }
}
 

deploy.sh

#!/bin/bash
echo "Deploying application..."
# Docker 컨테이너 실행
docker pull your-docker-image
docker stop my-app || true
docker rm my-app || true
docker run -d --name my-app -p 8080:8080 your-docker-image
 

설명:

  • Docker Build: Docker 이미지를 빌드합니다.
  • Docker Push: Docker 이미지를 Docker Hub에 푸시합니다.
  • deploy.sh: Docker 컨테이너를 실행하는 배포 스크립트입니다.
    • docker pull: Docker 이미지를 Docker Hub에서 가져옵니다.
    • docker stop: 실행 중인 컨테이너를 중지합니다.
    • docker rm: 중지된 컨테이너를 삭제합니다.
    • docker run: 새로운 Docker 컨테이너를 실행합니다.

16.3.2 모니터링과 로깅

애플리케이션의 상태를 모니터링하고, 로그를 분석하여 문제를 해결할 수 있습니다.

예제: Spring Boot Actuator와 ELK 스택을 이용한 모니터링과 로깅

  1. Spring Boot Actuator 설정

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
 

application.yml

management:
  endpoints:
    web:
      exposure:
        include: '*'
 

설명:

  • spring-boot-starter-actuator: Spring Boot 애플리케이션의 상태를 모니터링할 수 있는 Actuator 기능을 제공합니다.
  • management.endpoints.web.exposure.include: 모든 Actuator 엔드포인트를 노출합니다.
  1. ELK 스택 설정
    • ELK 스택(Elasticsearch, Logstash, Kibana)을 설치하여 로그를 수집하고 분석합니다.
    • Logstash를 설정하여 애플리케이션 로그를 Elasticsearch로 전송하고, Kibana에서 시각화합니다.

logstash.conf

input {
  file {
    path => "/path/to/logs/*.log"
    start_position => "beginning"
  }
}

filter {
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:loglevel} %{JAVALOGMESSAGE:message}" }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "application-logs"
  }
  stdout { codec => rubydebug }
}
 

설명:

  • input: 로그 파일을 입력으로 설정합니다.
  • filter: 로그 메시지를 파싱하여 구조화합니다.
  • output: Elasticsearch로 로그를 전송합니다.

예제 코드: ELK 스택을 이용한 로깅

logback-spring.xml

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>/path/to/logs/application.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>
 

설명:

  • ConsoleAppender: 로그를 콘솔에 출력합니다.
  • FileAppender: 로그를 파일에 기록합니다.
  • pattern: 로그 메시지의 포맷을 정의합니다.

추가 예제: Prometheus와 Grafana를 이용한 모니터링

Prometheus 설정

prometheus.yml

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'spring-boot'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080', 'localhost:8081']
 

Spring Boot 설정

pom.xml

<dependencies>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
</dependencies>
 

application.yml

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  metrics:
    export:
      prometheus:
        enabled: true
 

Grafana 설정

  1. Grafana 설치 및 설정
    • Grafana를 설치하고 실행합니다.
    • Prometheus를 데이터 소스로 추가합니다.
  2. 대시보드 구성
    • Grafana에서 Prometheus 데이터를 시각화할 대시보드를 구성합니다.

추가 예제: Filebeat와 ELK 스택 구성

filebeat.yml

filebeat.inputs:
- type: log
  paths:
    - /path/to/logs/*.log

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "spring-boot-logs-%{+yyyy.MM.dd}"
 

설명:

  • Filebeat: 로그 파일을 수집하여 Elasticsearch로 전송합니다.
  • output.elasticsearch: Elasticsearch로 로그를 전송합니다.
    • index: 로그 데이터를 저장할 인덱스를 지정합니다.

Spring Boot 설정

logback-spring.xml

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>/path/to/logs/application.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>
 

설명:

  • Filebeat: 로그 파일을 수집하여 Elasticsearch로 전송합니다.
  • logback-spring.xml: 로그 설정 파일로, 로그를 콘솔과 파일에 기록합니다.
  • Kibana: 로그 데이터를 시각화하고 분석할 수 있는 도구입니다.

Kibana 설정

  1. Kibana 설치 및 설정
    • Kibana를 설치하고 실행합니다.
    • Elasticsearch 인덱스를 설정하고 시각화할 대시보드를 구성합니다.
  2. Kibana 대시보드 구성
    • Filebeat에서 전송된 로그 데이터를 시각화합니다.
    • 다양한 차트와 그래프로 로그 데이터를 분석합니다.

이로써 실전 프로젝트의 웹 애플리케이션 개발, 마이크로서비스 아키텍처, 배포 및 유지보수 전략에 대해 구체적으로 설명했습니다. 이러한 내용들을 통해 실무에서 효과적으로 프로젝트를 관리하고 운영할 수 있을 것입니다.

 
 
반응형

'IT 강좌(IT Lectures) > Java' 카테고리의 다른 글

15강. 테스팅과 유지보수  (0) 2024.07.02
14강. 데이터베이스 연동  (0) 2024.07.01
13강. Spring 프레임워크  (0) 2024.06.30
12강. 보안과 암호화  (0) 2024.06.29
11강. Java 성능 최적화  (0) 2024.06.28