๐Ÿ—ž๏ธ IT ๋™ํ–ฅ ํŒŒ์•… ๋ฐ ๋‚˜์˜ ์ƒ๊ฐ ์ •๋ฆฌ

[Java] Java๋กœ ๊ฐ•๋ ฅํ•œ REST API ๊ตฌ์ถ• 4๊ฐ€์ง€ ํŒ

JanginTech 2024. 8. 25. 03:36

1. Four Essential Tips for Building a Robust REST API in Java

https://dzone.com/articles/four-tips-for-building-a-robust-rest-api-in-java

 

Four Tips for Building a Robust REST API in Java - DZone

Enhance your Java REST API with consistent resource naming, maintainable versioning, robust security, and proper exception handling.

dzone.com

 

 

[ ์š”์•ฝ ]

Java๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ•๋ ฅํ•œ REST API๋ฅผ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•œ ์ฃผ์š” ์ „๋žต์„ ์ œ๊ณตํ•œ๋‹ค.

 

1. ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ตฌ์กฐํ™”ํ•˜๊ธฐ โžก๏ธ ์œ ์šฉ์„ฑ ๋ฐ ์„œ์น˜ ๊ฐœ์„ 

์ผ๊ด€๋œ ์šฉ์–ด, ๋ช…๋ช… ๊ทœ์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„(DDD) ์›์น™์— ๋”ฐ๋ผ ์ฃผ์š” ๋„๋ฉ”์ธ์œผ๋กœ ์‹œ์ž‘ํ•˜์—ฌ ๋‹ค์Œ ํ•˜์œ„ ๋„๋ฉ”์ธ์œผ๋กœ ์„ธ๋ถ„ํ™”ํ•œ๋‹ค

 

์˜ˆ๋ฅผ ๋“ค์–ด,

GET /expeditions - ๋ชจ๋“  ๋ฐ์ดํ„ฐ search

GET /expeditions/{id} - ํŠน์ • id๋ฅผ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ๋ฅผ search

 

@Path("expeditions")
public class ExpeditionResource {
    @GET
    public List<Expedition> list() {
        // implementation here
    }

    @GET
    @Path("/{id}")
    public Expedition get(@PathParam("id") String id) {
        // implementation here
    }
    
    @GET
    @Path("/search")
    public List<Expedition> mine() {
        // implementation here
    }
}

* ๋” ์ž์„ธํ•œ ๊ฑด REST API ์„ค๊ณ„ ๊ทœ์น™์„ ์ฐพ์•„๋ด์•ผ ํ•œ๋‹ค.

 

2. ์œ ์ง€ ๋ณด์ˆ˜์„ฑ โžก๏ธ ํ™•์žฅ์„ฑ ๋ฐ ๋ฌธ์„œํ™”๋ฅผ ์œ„ํ•ด

2.1. ํฌ๊ด„์ ์ธ API ๋ฌธ์„œ ์ถ”๊ฐ€

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

๋ฌด์—‡๋ณด๋‹ค ์—†์–ด์„œ๋Š” ์•ˆ ๋  ์ค‘์š”ํ•œ ์ž‘์—…์ด๊ธฐ๋„ ํ•˜๋‹ค.

 

2.2.๋ฒ„์ „ ๊ด€๋ฆฌ

๋˜ ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ๋ฒ„์ „ ๊ด€๋ฆฌ๋‹ค. ๋ฒ„์ „ ๊ด€๋ฆฌ๋Š” ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ํŽธ์˜์— ๋”ฐ๋ผ ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค.

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

package os.expert.demo.expeditions.v1;
@Path("/api/v1/expeditions")
public class ExpeditionResource {
// implementation here
}


package os.expert.demo.expeditions.v2;
@Path("/api/v2/expeditions")
public class ExpeditionResource {
// implementation here
}

 

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

 

 

3. ์‚ฌ์šฉ์ž ๊ฒ€์ฆ ๋กœ์ง โžก๏ธ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ

๋งจ ์ฒ˜์Œ ๋ฒˆ์—ญ ๋Œ๋ ธ์„ ๋•Œ "์‚ฌ์šฉ์ž๋ฅผ ์ ˆ๋Œ€ ๋ฏฟ์ง€ ๋งˆ์„ธ์š”" ๋ผ๊ณ  ๋‚˜์™€ ๋†€๋ž๋‹ค..ใ…Žใ…Ž

๋ณด์•ˆ์€ ๋ชจ๋“  API์˜ ๊ธฐ๋ณธ์ ์ธ ์ธก๋ฉด์œผ๋กœ์„œ, ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ ˆ๋Œ€ ์‹ ๋ขฐํ•˜์ง€ ๋ง ๊ฒƒ์„ ๊ฐ•์กฐํ•œ๋‹ค.

@GET
@Path("/my-expeditions")
public List<Expedition> myExpeditions() {
    // No need to request IDs since the user is authenticated
    // implementation here
}

 

์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ID์—๋Š” ์˜์กดํ•˜๋˜, ์ž…๋ ฅ์„ ์ ˆ๋Œ€ ์‹ ๋ขฐํ•˜์ง€ ๋ง๊ณ  ๊ถŒํ•œ ์ธ์ฆ์„ ํ•„์ˆ˜์ ์œผ๋กœ ์ง„ํ–‰ํ•ด์•ผ ํ•œ๋‹ค.

๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ํ•ญ์ƒ ์‹œํ–‰ํ•˜์—ฌ ๊ถŒํ•œ์„ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š์€ ์ž๊ฐ€ ๋ฌด๋‹จ์œผ๋กœ ์ž‘์—…ํ•˜๋Š” ํ–‰์œ„๋ฅผ ๋ง‰์•„์•ผ ํ•œ๋‹ค.

 

 

4. ์ ์ ˆํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ โžก๏ธ API ์ผ๊ด€์„ฑ์„ ์œ ์ง€ 

์ž˜ ์„ค๊ณ„๋œ API๋Š” ์ ์ ˆํ•œ HTTP ์ƒํƒœ ์ฝ”๋“œ์— ๋งคํ•‘ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ”์–ด์•ผ ํ•œ๋‹ค.

@Provider
public class ExpeditionNotFoundExceptionMapper implements ExceptionMapper<ExpeditionNotFoundException> {
    @Override
    public Response toResponse(ExpeditionNotFoundException exception) {
        return Response.status(Response.Status.NOT_FOUND).entity(exception.getMessage()).build();
    }
}

 

์œ„ ์˜ˆ์‹œ์™€ ๊ฐ™์ด, ์‚ฌ์šฉ์ž ์ •์˜ ์˜ˆ์™ธ ๋งคํผ๋กœ, ์˜ˆ์™ธ๋ฅผ ํŠน์ • HTTP ์‘๋‹ต์— ๋งคํ•‘ํ•œ๋‹ค.

ExceptionMapper<ExpeditionNotFoundException> ๊ฐ€ ExpeditionNotFoundException ์— throw ๋˜๋ฉด,

์ด ๋งคํผ๋Š” HTTP 404 Not Found ์ƒํƒœ์™€ ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

 

1. ์ผ๊ด€๋œ ์šฉ์–ด ์‚ฌ์šฉ2. ์œ ์ง€๋ณด์ˆ˜์™€ ๋ฌธ์„œํ™”3. ๋ณด์•ˆ์„ ์ตœ์šฐ์„ ์œผ๋กœ!4. ์ ์ ˆํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ

 

์ด 4๊ฐ€์ง€๋ฅผ ์ง€ํ‚ค๋ฉด ๊ฒฌ๊ณ ํ•œ REST API๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋  ๊ฒƒ์ด๋‹ค.

 

 

 โž•

๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„(DDD)? 

  • ๊ธฐ๋ณธ ๋น„์ฆˆ๋‹ˆ์Šค ๋„๋ฉ”์ธ์„ ๊ธฐ๋ฐ˜์œผ๋กœ sw ๋ชจ๋ธ๋ง์— ์ดˆ์ ์„ ๋งž์ถ˜ sw ์„ค๊ณ„ ์ ‘๊ทผ ๋ฐฉ์‹
  • ๋„๋ฉ”์ธ ๋ชจ๋ธ: ์ฝ”๋“œ ์‹ค์ œ ๋„๋ฉ”์ธ์„ ๋ฐ˜์˜
  • ์œ ๋น„์ฟผํ„ฐ์Šค ์–ธ์–ด: ๊ฐœ๋ฐœ์ž์™€ ๋„๋ฉ”์ธ ์ „๋ฌธ๊ฐ€๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ณตํ†ต ์–ธ์–ด
  • ์ œํ•œ๋œ ์ปจํ…์ŠคํŠธ: ๋„๋ฉ”์ธ ๋‚ด์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๋ชจ๋ธ์„ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ช…ํ™•ํ•œ ๊ฒฝ๊ณ„ ์—ญํ• 
  • DDD๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋น„์ฆˆ๋‹ˆ์Šค ๋ชฉํ‘œ์— ๋งž์ถฐ ์กฐ์ •ํ•˜์—ฌ ์œ ์ง€ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค.

 

 


 

๐Ÿค” ์ด์— ๋Œ€ํ•œ ๋‚˜์˜ ์ƒ๊ฐ

์œ„ ๊ทœ์น™์ค‘ '๋ช‡ ๊ฐ€์ง€๋ฅผ ์ œ๋Œ€๋กœ ์ค€์ˆ˜ํ•˜์˜€๋Š”๊ฐ€?' ์ƒ๊ฐํ•ด ๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ํ•œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ์™€ ์ ์ ˆํ•œ ์‘๋‹ต ์ฝ”๋“œ ๋งคํ•‘์ด ํ•„์ˆ˜์ ์ด๋ผ๋Š” ์ ์—๋„ ๋™์˜ํ•œ๋‹ค. 

๊ทธ๋™์•ˆ ๋ฌธ์„œํ™”๋Š” ๋ฒˆ๊ฑฐ๋กญ๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ  'ํ•„์ˆ˜'๋ผ๊ธฐ๋ณด๋‹ค๋Š” ์‹œ๊ฐ„์ด ๋‚  ๋•Œ ์ฒ˜๋ฆฌํ•˜๋ฉด ์ข‹์„ '์„ ํƒ'์ด์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ์˜ค๋Š˜ ๊ธ€์„ ๋ณด๊ณ  ์ƒ๊ฐ์ด ๋‹ฌ๋ผ์กŒ๋‹ค.

 

๋ฌด์—‡๋ณด๋‹ค, "์‚ฌ์šฉ์ž๋ฅผ ์‹ ๋ขฐํ•˜์ง€ ๋ง ๊ฒƒ" ์ด ๋˜๊ฒŒ ์‹ ์„ ํ•˜๊ณ  ์ถฉ๊ฒฉ์ ์ด๋‹ค.

์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด ์กด์žฌํ•˜์ง€๋งŒ ๋ฌด์กฐ๊ฑด์ ์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ด์œ ๋Š” ์•…์˜์ ์ธ ๋ชฉ์ ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.(์ •ํ™•ํžˆ ๋งํ•˜๋ฉด ์‚ฌ์šฉ์ž๋ณด๋‹จ ๊ณต๊ฒฉ์ž์— ๊ฐ€๊น๊ฒ ์ง€..?)

์ด๋Ÿฌํ•œ ๊ด€์ ์˜ ๋ณ€ํ™” ํ†ตํ•ด ํ–ฅํ›„ ํ”„๋กœ์ ํŠธ์—์„œ ์ด๋Ÿฌํ•œ ์›์น™๋“ค์„ ์‹ ๊ฒฝ์จ์„œ ๊ตฌํ˜„ํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ๋‹ค์งํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค!