본문 바로가기

명사 美 비격식 (무리 중에서) 아주 뛰어난[눈에 띄는] 사람[것]

이론

REST API(아키텍처)의 특징

Uniform (유니폼 인터페이스) 

항상 같은형식, 같은 방법으로 정보를 요청/수정한다.

자원 조회: GET /users/1
자원 생성: POST /users
자원 수정: PUT /users/1
자원 삭제: DELETE /users/1

 

Stateless (무상태성)

세션/쿠키정보를 별도로 저장하지않아 요청만을 단순히 처리한다.

 

Cacheable (캐시 가능)

HTTP 헤더값을 이용하여 리소스가 변경되었는지 확인한다.

JAVA에서는 먼저 SpringBoot를 사용하여 RESTful API를 만들고, Last-Modified 헤더와 E-Tag를 활용해 구현한다.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;
@RestController
@SpringBootApplication
public class CacheExampleApplication {

    private final String cachedData = "{\"id\": 1, \"name\": \"John Doe\", \"email\": \"johndoe@example.com\"}";
    private long lastModifiedTime = Instant.now().toEpochMilli();
    private final String eTag = "\"1\""; // 리소스의 고유 식별자 (E-Tag)

    @GetMapping("/user")
    public ResponseEntity<String> getUser(@RequestHeader(value = "If-None-Match", defaultValue = "") String ifNoneMatch,
                                          @RequestHeader(value = "If-Modified-Since", defaultValue = "") String ifModifiedSince) {
        
        // E-Tag 확인
        if (eTag.equals(ifNoneMatch)) {
        	// 수정되지 않았음을 나타내는 응답 (Not Modified)	
            return ResponseEntity.status(304).build(); 
        }
        
        // Last-Modified 헤더 확인
        long ifModifiedSinceTime = Instant.parse(ifModifiedSince).toEpochMilli();
        if (ifModifiedSinceTime >= lastModifiedTime) {
        	// 수정되지 않았음을 나타내는 응답 (Not Modified)
            return ResponseEntity.status(304).build(); 
        }

        // 리소스가 수정되었음을 나타내는 응답을 전송하고, 업데이트된 Last-Modified와 E-Tag 헤더를 함께 포함
        return ResponseEntity.ok()
                .header("Last-Modified", Instant.ofEpochMilli(lastModifiedTime).toString())
                .header("E-Tag", eTag)
                .body(cachedData);
    }

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

 

 

Self-descriptiveness (자체 표현 구조) 

RESTFUL API 메세지만 봐도 쉽게 이해할 수 있는 구조이다.

{
  "id": 1,
  "username": "john_doe",
  "email": "johndoe@example.com",
  "age": 30,
  "is_active": true
}

 

Client-Server 구조 

REST서버는 API를 제공, 클라이언트는 세션/로그인 정보로 각각의 역할이 확실히 구분된다.

 

구조적인 유연성을 확보하는 방법 가능

계층형구조 보안, 로드밸런싱, 암호화 계층을 추가해 구조상 유연성을 둘수있다.

<dependencies>
    <!-- Spring Boot Starter Web for RESTful API -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Security for security features -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- Spring Cloud Starter Netflix Ribbon for load balancing -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
    <!-- Jasypt for encryption -->
    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
    </dependency>
</dependencies>
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@RibbonClient(name = "example-service", configuration = RibbonConfiguration.class)
public class RestfulServiceApplication {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

class RibbonConfiguration {
    // Ribbon 구성이 필요한 경우 여기에 추가
}

@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/public/**").permitAll() // /public/** 경로는 인증 없이 접근 가능
            .anyRequest().authenticated()
            .and()
            .httpBasic(); // 기본 HTTP 인증 사용
    }
}

@RestController
class ApiController {
    
    @Value("${secret.message}")
    private String secretMessage;

    private final RestTemplate restTemplate;

    public ApiController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("/public/resource")
    public String publicResource() {
        return "이것은 공개 리소스입니다.";
    }

    @GetMapping("/secure/resource")
    public String secureResource() {
        return "이것은 보안 리소스입니다. 비밀 메시지: " + secretMessage;
    }

    @GetMapping("/load-balanced/resource")
    public String loadBalancedResource() {
        // 로드 밸런싱된 방식으로 다른 서비스에 접근
        String response = restTemplate.getForObject("http://example-service/resource", String.class);
        return "다른 서비스로부터의 응답: " + response;
    }
}

 

네트워크 기반의 중간매체

Proxy, Gateway는 클라이언트와 서버 사이에 위치하여 요청과 응답을 중개/가공함

<dependencies>
    <!-- Spring Boot Starter Web for RESTful API -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Cloud Starter Netflix Zuul for API Gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

//api gateway의 메인 애플리케이션 클래스를 만든다.

@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {

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