๐Ÿ’ป
Albert's Til
GitHub
  • ๋งค์ผ๋งค์ผ ์กฐ๊ธˆ์”ฉ ์„ฑ์žฅํ•˜๊ธฐ
    • README
    • CS
      • Network
      • HTTP
        • NO-CACHE
      • ์˜ค๋ฅ˜ ์ฝ”๋“œ
      • ORM ๋„๊ตฌ
      • Design Pattern
        • CQRS Pattern
          • Event Sourcing and CQRS pattern
        • Builder Pattern
    • DB
      • MySQL
        • Timeline
        • Pagination
        • Index
        • Database Performance Optimization Strategies
        • B+ tree
        • MySQL Connectors VS MySQL Shell(Scripting) VS MySQL Workbench
        • MySQL Storage Engine Architecture
      • Normalization & Denormalization
      • JPA
        • @Transactional
        • Why JPA?
        • About JPA
        • N+1 Issue
        • Index
        • ElementCollection&CollectionTable
        • orphanRemoval
        • CascadeType
        • Use Subselect
        • Dynamic Instance Creation
        • Paging
        • Order
        • Spefication
        • mappedBy
      • MongoDB
        • ObjectId
      • Why MySQL?
      • ACID properties of transactions
      • Between JPA and JDBC
      • Identifiers in Hibernate/JPA
    • Java
      • Jackson de/serialize
      • Collections.singletonList() vs List.of()
      • Manage dependencies in Gradle
      • Logging Level
      • Bean Validation
      • JVM Internals
        • Threads
          • Frame
        • Shared Between Threads
          • Classloader
            • Class Loader Hierarchy
            • Loading Linking Initialization
      • Java Collection Framework
      • Annotation
      • Generic
      • ๋””๋ฏธํ„ฐ ๋ฒ•์น™
    • Spring
      • Caching
      • Spring Integration Overview
        • ThreadPollTaskExecutor
        • Messaging Bridge
        • Channel Adapter
        • Poller
        • Configuration and @EnableIntegration
        • Message Endpoints
        • Message Channels
      • HATEOAS
      • @Autowired vs Constructor Dependency Injection
      • Spring Security
        • JWT ํ† ํฐ ์‚ฌ์šฉํ•œ ์ธ๊ฐ€
        • OAuth 2 Login
        • OAuth 2 ์ธ์ฆ
        • ์ธ๊ฐ€
        • ์ธ์ฆ
        • PasswordEncoder
      • IoC Container
      • Filter,Interceptor,AOP,Argument Resolver
      • Spring Annotation
      • About Spring
    • Kafka
      • Error Channel
    • Infra
      • Scale Up || Scale Out
      • Docker
        • Dockerfile
        • Docker Hub Deploy
        • Command
      • Cloud ์œ ํ˜•
        • Infrastructure as a Service
        • Platform as a Service
        • Software as a Service
      • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ
        • ์—”์ง„์—‘์Šค(Nginx)
      • ์ฝ”๋“œ ์ž๋™ ๋ฐฐํฌ
        • Technical
      • AWS EC2
        • PEM(Privacy Enhanced Mail) ํ‚ค
      • AWS RDS
      • AWS S3
    • CodeSquad
      • Spring Boot Project 1์ฃผ์ฐจ ํšŒ๊ณ 
      • Spring Boot Project 2์ฃผ์ฐจ ํšŒ๊ณ 
      • Spirng Boot Project 3์ฃผ์ฐจ ํšŒ๊ณ 
      • Spring Boot Project 4์ฃผ์ฐจ ํšŒ๊ณ 
    • Foody Moody ํ”„๋กœ์ ํŠธ
      • Query Performance Comparison
      • HeartCount Asynchronous Issue
      • DeferredResult
      • ResponseBodyEmitter
      • SseEmitter (Spring)
      • Server-Sent Events (SSE)
      • ๊ธฐ์ˆ  ์Šคํƒ ์ ์šฉ ์ด์œ 
      • NO-CACHE(HTTP)
      • Transactional
    • DDD
      • AggregateId
    • Test
      • RestAssured
    • Coding and Algorithmic Problems
      • 819. Most Common Word
      • 344. Reverse String
      • 125. Valid Palindrome
      • 937. Reorder Data in Log Files
    • Node
      • Async... Await...
      • Custom Transactional Decorator Challenger
    • Python
      • Python Basic Grammar
        • Comments and Input/Output
        • Variable
        • Data type
        • Operations and syntax
        • List,Tuple,Dictionary,Set
        • Function
        • Conditional statement
        • Loop
    • HTML
      • HTML Basic
      • HTML Basic Tags
      • HTML Form Tags
      • HTML Table Tags
    • CSS
      • CSS Basic
      • CSS Practice
Powered by GitBook
On this page
  • Example
  • Requset
  • Response
  • CODE
  • ์žฅ๋‹จ์ 
  • ์žฅ์ 
  • ๋‹จ์ 
  • Reference

Was this helpful?

  1. ๋งค์ผ๋งค์ผ ์กฐ๊ธˆ์”ฉ ์„ฑ์žฅํ•˜๊ธฐ
  2. Spring

HATEOAS

Hypermedia as the engine of application state

Hypermedia as the engine of application state (HATEOAS) is a constraint of the REST application architecture that distinguishes it from other network application architectures.

  • Hypermedia,

    • an extension of the term hypertext, is a nonlinear medium of information that includes graphics, audio, video, plain text and hyperlinks.

  • Hyperlink,

    • In computing, a hyperlink, or simply a link, is a digital reference to data that the user can follow or be guided to by clicking or tapping

Example

user-agent๋Š” ์ง„์ž…์  URL์„ ํ†ตํ•ด REST API์— HTTP ์š”์ฒญ์„ ํ•ฉ๋‹ˆ๋‹ค.

user-agent๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํ›„์† ์š”์ฒญ์€ ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ๋‚ด์—์„œ ๋ฐœ๊ฒฌ๋ฉ๋‹ˆ๋‹ค.

Requset

GET /accounts/12345 HTTP/1.1
Host: bank.example.com

Response

HTTP/1.1 200 OK

{
    "account": {
        "account_number": 12345,
        "balance": {
            "currency": "usd",
            "value": 100.00
        },
        "links": {
            "deposits": "/accounts/12345/deposits",
            "withdrawals": "/accounts/12345/withdrawals",
            "transfers": "/accounts/12345/transfers",
            "close-requests": "/accounts/12345/close-requests"
        }
    }
}

CODE

HATEOAS ์ ์šฉ์ „

@GetMapping
public ResponseEntity<Page<Product>> findAll(Principal principal,
        Pageable pageable) {
    Page<Product> products =
            productDao.findProductsByUserEmail(principal.getName(), pageable);
    return ResponseEntity.ok(products);
}
HTTP/1.1 200 OK
Content-Type: application/hal+json
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 9362

{
  "_embedded" : {
    "productResponseList" : [ {
      "productId" : "648dcfb047d916749c554cd4",
      "productName" : "testProductName",
      "price" : 10000,
      "description" : "testDescription",
      "brand" : "testBrand",
      "category" : "testCategory",
      "createdTime" : "20230618002224",
      "updateTime" : "20230618002224",
    }, {
      "productId" : "648dcfb047d916749c554cd5",
      "productName" : "testProductName",
      "price" : 10000,
      "description" : "testDescription",
      "brand" : "testBrand",
      "category" : "testCategory",
      "createdTime" : "20230618002224",
      "updateTime" : "20230618002224",
    }
   ........
   ........
   ........
    , {
      "productId" : "648dcfb047d916749c554ce7",
      "productName" : "testProductName",
      "price" : 10000,
      "description" : "testDescription",
      "brand" : "testBrand",
      "category" : "testCategory",
      "createdTime" : "20230618002224",
      "updateTime" : "20230618002224",
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 100,
    "totalPages" : 5,
    "number" : 1
  }
}

HATEOAS ์ ์šฉํ›„

@GetMapping
public ResponseEntity<PagedModel<ProductResponse>> findAll(Principal principal,
    Pageable pageable) {
Page<Product> products =
        productDao.findProductsByUserEmail(principal.getName(), pageable);

PagedModel<ProductResponse> productResponses = pagedResourcesAssembler
        .toModel(products, productAssembler);

return ResponseEntity.ok(productResponses);
}

@RequiredArgsConstructor
@RestController
@RequestMapping(path = "/products", produces = MediaTypes.HAL_JSON_VALUE)
public class ProductController {

    private final ProductService productService;
    private final StoreDao storeDao;
    private final ProductDao productDao;
    private final PagedResourcesAssembler<Product> pagedResourcesAssembler;
    private final ProductAssembler productAssembler;

    @PostMapping
    public ResponseEntity<CreatedProductResponse> addProduct(
            @RequestBody ProductRequest productRequest,
            Principal principal) {
        StoreId storeId = storeDao.findStoreIdByUserEmail(principal.getName());
        CreatedProductResponse productResponse = productService.addProduct(productRequest, storeId);

        ProductId productId = productResponse.getProductId();
        Link selfRel = BusinessLinks.getProductSelfRel(productId);
        productResponse.add(selfRel, BusinessLinks.MY_STORE);
        return ResponseEntity.created(selfRel.toUri()).body(productResponse);
    }


    @GetMapping
    public ResponseEntity<PagedModel<ProductResponse>> findAll(Principal principal,
            Pageable pageable) {
        Page<Product> products =
                productDao.findProductsByUserEmail(principal.getName(), pageable);

        PagedModel<ProductResponse> productResponses = pagedResourcesAssembler
                .toModel(products, productAssembler);

        return ResponseEntity.ok(productResponses);
    }
}
HTTP/1.1 200 OK
Content-Type: application/hal+json
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 9362

{
  "_embedded" : {
    "productResponseList" : [ {
      "productId" : "648dcfb047d916749c554cd4",
      "productName" : "testProductName",
      "price" : 10000,
      "description" : "testDescription",
      "brand" : "testBrand",
      "category" : "testCategory",
      "createdTime" : "20230618002224",
      "updateTime" : "20230618002224",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/products/648dcfb047d916749c554cd4"
        }
      }
    }, {
      "productId" : "648dcfb047d916749c554cd5",
      "productName" : "testProductName",
      "price" : 10000,
      "description" : "testDescription",
      "brand" : "testBrand",
      "category" : "testCategory",
      "createdTime" : "20230618002224",
      "updateTime" : "20230618002224",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/products/648dcfb047d916749c554cd5"
        }
      }
    }
    ........
    ........
    ........
    , {
      "productId" : "648dcfb047d916749c554ce7",
      "productName" : "testProductName",
      "price" : 10000,
      "description" : "testDescription",
      "brand" : "testBrand",
      "category" : "testCategory",
      "createdTime" : "20230618002224",
      "updateTime" : "20230618002224",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/products/648dcfb047d916749c554ce7"
        }
      }
    } ]
  },
  "_links" : {
    "first" : {
      "href" : "http://localhost:8080/products?page=0&size=20"
    },
    "prev" : {
      "href" : "http://localhost:8080/products?page=0&size=20"
    },
    "self" : {
      "href" : "http://localhost:8080/products?page=1&size=20"
    },
    "next" : {
      "href" : "http://localhost:8080/products?page=2&size=20"
    },
    "last" : {
      "href" : "http://localhost:8080/products?page=4&size=20"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 100,
    "totalPages" : 5,
    "number" : 1
  }
}

์žฅ๋‹จ์ 

์žฅ์ 

ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ ๋…๋ฆฝ์„ฑ

  • HATEOAS๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์˜ ๊ฐ•๋ ฅํ•œ ๊ฒฐํ•ฉ์„ ํ”ผํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•œ API ๋ณ€๊ฒฝ์˜ ์—ฌํ–ฅ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ํ•˜์ดํผ๋ฏธ๋””์–ด ๋งํฌ๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ , ์„œ๋ฒ„์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์†Œ์Šค ํ‘œํ˜„์— ๋Œ€ํ•ด ์‚ฌ์ „ ์ง€์‹์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋กœ์จ ์„œ๋ฒ„ ์ธก์˜ ๋ณ€๊ฒฝ์—๋„ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

APIํƒ์ƒ‰ ๋ฐ ๋””์Šค์ปค๋ฒ„๋ฆฌ

  • HATEOA๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ API์˜ ๊ตฌ์กฐ์™€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ž‘์—…์„ ๋™์ ์œผ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ์‹œ์ž‘์  ๋ฆฌ์†Œ์Šค๋กœ๋ถ€ํ„ฐ ์ถœ๋ฐœํ•˜์—ฌ ํ•˜์ดํผ๋ฏธ๋””์–ด ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์„œ๋ฒ„๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์†Œ์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” API๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ๋ฐœ๊ฒฌํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ฃผ๋ฉฐ, ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์—์„œ ํ•˜๋“œ ์ฝ”๋”ฉ๋œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์œ ์—ฐ์„ฑ๊ณผ ํ™”์žฅ์„์„ ๋†’์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์…€ํ”„ ์„ค๋ช…ํ˜• API

  • HATEOAS๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•˜์ดํผ๋ฏธ๋””์–ด ๋งํฌ์™€ ํ•จ๊ป˜ ๋ฆฌ์†Œ์Šค ํ‘œํ˜„์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด API๊ฐ€ ์ž๊ธฐ ์„ค๋ช…์ (self-descriptive)์ด ๋˜์–ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฆฌ์†Œ์Šค์˜ ์˜๋ฏธ์™€ ์ƒํ˜ธ์ž‘์šฉ ๋ฐฉ๋ฒ•์„ ์ดํ•ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ API๋ฌธ์„œ๋ฅผ ์ฝ๊ณ  ํ•ด์„ํ•˜๋Š” ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ 

๋ณต์žก์„ฑ

  • HATEOA๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด API ์„ค๊ณ„์™€ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๋ฅผ ๋” ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ํ•˜์ดํผ๋ฏธ๋””์–ด ๋งํฌ๋ฅผ ์ดํ•ดํ•˜๊ณ  ํ•ด์„ํ•˜๋Š” ๋…ธ๋ฆฌ๋ฅผ ์ถ”๊ฐ€๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋ฉฐ, ์„œ๋ฒ„๋А ๋งํฌ๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ ๋ฐ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋„คํŠธ์›Œํฌ ์˜ค๋ฒ„ํ—ค๋“œ

  • HATEOA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ํ•˜์ดํผ๋ฏธ๋””์–ด ๋งํฌ๊ฐ€ ๋ฆฌ์†Œ์Šค ํ‘œํ˜„์— ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” API ์‘๋‹ต์˜ ํฌ๊ธฐ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ฌ์ˆ˜ ์žˆ๊ณ , ๋„คํŠธ์›Œํฌ ์ „์†ก ์‹œ๊ฐ„๊ณผ ๋Œ€์—ญํญ์„ ์†Œ๋น„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ๊ตฌํ˜„์˜ ์–ด๋ ค์›€

  • HATEOAS๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํด๋ผ์ด์–ธํŠธ๋Š” ๋งํฌ์™€ ๋ฆฌ์†Œ์Šค ์ƒํ˜ธ์ž‘์šฉ์„ ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ธก์— HATEOAS๋ฅผ ์ง€์›ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋„์ž…ํ•ด์•ผ ํ•˜๋ฉฐ, ๊ธฐ์กด์˜ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๋ถ€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ถ”๊ฐ€ ์ž‘์—…์œผ๋กœ ๋‹ค๊ฐ€์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Reference

Last updated 1 year ago

Was this helpful?

HATEOASWikipedia
Logo