04.28 학습

한강섭·2025년 4월 28일
0

학습 & 숙제

목록 보기
77/103
post-thumbnail

REST API 정리


REST API 개요

최근의 서비스/애플리케이션의 개발 흐름:

  • 플랫폼 종속적 → 모두가 공통적으로 사용하는 HTTP Protocol (REST API) 기반
  • 멀티 플랫폼, 멀티 디바이스 시대로 변화

REST (REpresentational State Transfer)

  • Representational: 자원의 표현으로 JSON, XML 등의 형식으로 표현됨
  • State: 애플리케이션의 상태
  • Transfer: 네트워크를 통해 상태를 전송함

2000년 로이 필딩이 제안한 아키텍처 스타일

주요 특징

  • 자원 중심: 모든 것은 자원으로 표현되며 각 자원은 고유한 URI를 갖는다
  • HTTP 메서드 활용: 요청을 위해 HTTP의 GET/POST/PUT/DELETE 등을 사용한다
  • 자원의 표현: 자원은 JSON, XML을 사용한다
  • 무상태(Stateless): 각 요청은 이전 요청과 독립적이며 서버는 클라이언트의 상태를 저장하지 않는다

기존 방식 → REST 방식

기존 방식REST 방식
/getUserInfo?id=123/users/123 (자원 중심)
주로 GET과 POST만 사용GET, POST, PUT, DELETE 등 활용
서버에 세션 상태 유지 경향Stateless: 각 요청은 독립적
HTMLJSON, XML 등 표준화된 형식
-클라이언트-서버가 명확히 분리됨
확장성 제한(높은 결합도)확장성 높음(분산 시스템에 적합)
-URI에 버전정보 포함 가능 (예: /api/v1/users)

작성 권장 사항

  • 자원 중심의 URL 사용: URL은 자원을 나타내며 명사 형태로 작성
  • HTTP 메서드 사용: URL 자체는 자원의 위치를 나타내며 HTTP 메서드로 작업의 의미 전달
  • 계층 구조 반영: members/{mno}/addresses 자원의 관계를 URL에 반영하여 계층 구조 표현
  • 목록과 개별 자원의 구분: /members, /members/{mno}
  • 버전 관리: API 버전을 URL에 포함하여 변경 사항에 유연하게 대응
  • 소문자 사용
  • 쿼리스트링 활용: 필터링, 정렬 등 추가적인 정보는 쿼리 스트링으로 전달

REST API URL

같은 URL에 다양한 요청 방식 사용 예시:

  • http://example.com/api/members/123에 대한
  • POST, PUT, PATCH, DELETE 방식의 차이

Spring에서의 REST API 관련 어노테이션

@PathVariable

URL 상의 변수를 처리하기 위한 어노테이션, 자동 형 변환 지원

@GetMapping("/members/${mno}")
@ResponseBody
public Map<String, ?> getMember(@PathVariable int mno) {
    // ...
}

어노테이션 구조:

@Target(ElementType.PARAMETER)
public @interface PathVariable {
    @AliasFor("value")
    String name() default "";
    boolean required() default true;
}

@ResponseBody

일반 @Controller에서 REST 서비스를 위해 사용

  • 데이터만 전송
  • MessageConverter가 데이터를 JSON등으로 가공해서 전달
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ResponseBody {}

@RestController

@Controller이면서 @ResponseBody 기능을 함께 제공

  • Controller의 모든 메서드 반환은 뷰가 아닌 데이터
  • 모든 요청 처리 메서드는 @ResponseBody를 갖는다
@Target(ElementType.TYPE)
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(annotation = Controller.class)
    String value() default "";
}

@RequestBody

body로 전송된 JSON 데이터를 객체로 변환

  • 파라미터를 통해 전달되는 데이터를 처리하는 @ModelAttribute와 유사한 역할
@Target(ElementType.PARAMETER)
public @interface RequestBody {
    boolean required() default true;
}

REST API 작성 예시

RestController 예시

@RestController
@RequestMapping("/api/v1/members")
@RequiredArgsConstructor
@Slf4j
public class MemberRestController {

    private final BasicMemberService mService;

    @PostMapping
    private Map<String, ?> registMember(@ModelAttribute Member member) {
        try {
            mService.registMember(member);
            return Map.of("status", "SUCCESS", "member", member);
        } catch (DataAccessException e) {
            e.printStackTrace();
            return Map.of("status", "FAIL", "error", e.getMessage());
        }
    }
}

Talend API Tester를 통한 테스트:


Swagger

REST API에 대한 자동 문서 생성/관리 시스템

주요 특징

  • API 문서화 어노테이션을 기반으로 자동으로 API 문서 생성
  • 웹 기반: 사용자가 문서화된 API를 통해 직접 요청을 실행하고 응답 확인 가능
  • 기본 문서 경로: http://server:port/context-path/swagger-ui.html

Document 작성 예시

@Configuration
// 문서에 대한 설명
@OpenAPIDefinition(info = @Info(title = "API 명세서 TEST", description = "API 명세서 테스트 입니다.", version = "v1"))
public class SwaggerConfig {

    // 관련된 API들의 grouping
    @Bean
    GroupedOpenApi memberOpenApi() {
        String[] paths = { "/api/v1/members/**" };
        return GroupedOpenApi.builder().group("Member 관련 API").pathsToMatch(paths).build();
    }

    @Bean
    GroupedOpenApi otherOpenApi() {
        String[] paths = { "/api/etc/**" };
        return GroupedOpenApi.builder().group("기타 API").pathsToMatch(paths).build();
    }
}

Controller Swagger 설정 예시

@RestController
@RequestMapping("/api/v1/members")
@Tag(name = "AuthRestController", description = "회원 관리를 위한 기능 제공")
public class AuthRestController implements RestControllerHelper {

    @GetMapping
    @Operation(summary = "회원 목록 조회", description = "모든 회원의 정보를 반환한다.")
    @ApiResponses({ 
        @ApiResponse(responseCode = "200", description = "회원 목록 조회 성공"), 
        @ApiResponse(responseCode = "500", description = "회원 목록 조회 실패")
    })
    private ResponseEntity<?> memberList(@ModelAttribute SearchCondition condition, Model model) {
        try {
            Page<Member> page = mService.search(condition);
            return handleSuccess(Map.of("result", page));
        } catch (DataAccessException e) {
            return handleFail(e);
        }
    }   
}

DTO Swagger 설정 예시

@Schema(title = "Address(주소)", description = "회원의 주소 정보")
public class Address {
    @Schema(description = "주소번호로 저장 시 자동 채번", requiredMode = RequiredMode.NOT_REQUIRED)
    private int ano;
    @Schema(description = "회원번호", requiredMode = RequiredMode.REQUIRED)
    private int mno;
}

RestTemplate

개요

크로스 도메인 요청 처리를 위한 도구

SOP(Same origin Policy : 동일 근원 정책)

  • JavaScript 단에서 Ajax 사용 시 사용 문서와 동일한 Origin으로만 데이터 전송 허용

CORS: 서로 다른 domain 간의 리소스 공유 설정

  • @CrossOrigin: 컨트롤러 별 설정
  • WebMvcConfigurer를 통한 전역 설정

RestClient 프로그래밍

JavaScript를 통한 요청이 문제이며 일반 요청은 문제되지 않음

  • JavaScript 요청을 내 서버로 하고 내 서버에서 원격지로 일반 요청 진행
  • Rest 서비스의 Client를 위해 RestTemplate 이용

RestTemplate 기능

Controller에서 REST API를 호출하기 위한 template

  • HTTP 클라이언트 라이브러리를 통해 높은 수준의 API 제공

주요 메서드 (xxx는 get/post)

  • xxxForObject: 간단한 요청에 최적화, HTTP 응답 본문만 반환하며 응답 헤더나 상태 코드는 제공하지 않음
  • xxxForEntity: 간단한 요청을 이용하는데, HTTP 응답의 모든 정보를 포함한 ResponseEntity 객체 반환, 응답 상태코드, 헤더, 본문을 포함
  • exchange: 좀 더 복잡한 HTTP 요청을 처리할 때 사용, get/post 뿐 아니라 put, delete 등을 지원, 요청 헤더와 본문 설정 가능

활용 예시

@RestController
@RequestMapping("/api/v1/etc")
@RequiredArgsConstructor
@Tag(name = "EtcRestController", description = "기타 유틸리티성 기능 제공")
public class EtcRestController implements RestControllerHelper {
        
    private final RestTemplate restTemplate;
    @Value("${key.vworld}")
    private String vworldKey;

    @GetMapping("/sidos")
    @Operation(summary = "시/도조회", description = "국토교통부 디지털트윈 국도에서 시/도 정보 조회")
    private ResponseEntity<?> getSido() {
        try {
            StringBuilder url = new StringBuilder("https://api.vworld.kr/ned/data/admCodeList?");
            url.append("format=json&").append("numOfRows=10&domain=localhost&").append("key=").append(vworldKey);
            Map<String, Object> result = restTemplate.getForObject(new URI(url.toString()), Map.class);
            return handleSuccess(result);
        } catch (Exception e) {
            return handleFail(e);
        }
    }
}

profile
기록하고 공유하는 개발자

0개의 댓글