์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Java + SpringBoot + Mongo DB ๋กœ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๊ฐ๊ฐ ์‚ฌ์šฉํ•  ๋ฒ„์ „์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

- Java 8 (์˜ค๋ผํดjdk๊ฐ€ ์ƒ์šฉ๋ชฉ์ ์œผ๋กœ๋Š” ์œ ๋ฃŒํ™”๋˜์–ด ์ถ”ํ›„ ์ฝ”ํ‹€๋ฆฐ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ด์•ผ๊ฒ ๋„ค์š”)

- Springboot 2.4.0 (20๋…„ 11์›” ํ˜„์žฌ ์ตœ์‹ ๋ฒ„์ „)

- Mongo DB 4.4.2 Community Server (20๋…„ 11์›” ํ˜„์žฌ ์ตœ์‹ ๋ฒ„์ „)

 

๋ชฝ๊ณ ๋””๋น„ ์„ค์น˜ ๊ด€๋ จํ•ด์„œ๋Š” ๋ชฝ๊ณ DB ์ตœ์‹ ๋ฒ„์ „ ์„ค์น˜ํ•˜๊ธฐ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์„ค์น˜ ํ›„ ์‚ฌ์šฉ์ž ๊ณ„์ • ์ƒ์„ฑ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์ƒ์„ฑ ๊ด€๋ จํ•ด์„œ๋Š” [๋ชฝ๊ณ DB] ๊ธฐ๋ณธ๋ช…๋ น์–ด ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

 

์ด์ œ ๊ทธ๋ž˜๋“ค ์ž๋ฐ” ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•˜์—ฌ Blog > ๋‹ค์ด์–ด๋ฆฌ ๋ฉ”๋‰ด์—์„œ ์‚ฌ์šฉํ•  CRUD๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋จผ์ € ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ์„ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ ๋ฐฑ์—”๋“œ ํ”„๋กœ์ ํŠธ

 

์šฐ์„  build.graldeํŒŒ์ผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

plugins {
    id "org.springframework.boot" version "2.4.0"
    id 'java'
}

group 'com.keichee'
version '1.0-SNAPSHOT'
sourceCompatibility = "1.8"

repositories {
    jcenter()
}

ext {
    springVersion = '2.4.0'
}

dependencies {
    testImplementation group: 'junit', name: 'junit', version: '4.12'

    implementation 'org.mongodb:mongodb-driver-sync:4.1.1'
    testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'

    implementation "org.springframework.boot:spring-boot-starter-web:$springVersion"
    testImplementation("org.springframework.boot:spring-boot-starter-test:$springVersion") {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

    compileOnly 'org.projectlombok:lombok:1.18.16'
    annotationProcessor 'org.projectlombok:lombok:1.18.16'

    testCompileOnly 'org.projectlombok:lombok:1.18.16'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.16'

    implementation "io.springfox:springfox-boot-starter:3.0.0"
    implementation 'io.springfox:springfox-swagger-ui:3.0.0'

    implementation 'com.google.guava:guava:30.0-jre'
}

test {
    useJUnitPlatform()
}

 

mongodb-driver ๋Š” sync 4.1.1 ์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๋กœ๊น…์„ ์œ„ํ•ด์„œ logback-classic์„ ์ถ”๊ฐ€ํ–ˆ๊ณ  

api ๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋„๋ก spring-boot-starter-web์„ ์ถ”๊ฐ€

getter, setter ๋“ฑ์˜ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์„ ์œ„ํ•ด lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๊ณ 

์Šค์›จ๊ฑฐ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  springfox ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์ด์ œ ๋ชฝ๊ณ DB์™€ ์—ฐ๊ฒฐ์„ ์œ„ํ•œ ์„ค์ •์„ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด์ค๋‹ˆ๋‹ค.

@Configuration
public class MongoConfig {

    private static final String host = "localhost";
    private static final int port = 27017;
    private static final String database = "blogapp";

    @Bean
    public MongoDatabase blogAppDatabase() {
        MongoClient client = MongoClients.create(
                MongoClientSettings.builder()
                        .applyToClusterSettings(builder ->
                                builder.hosts(Collections.singletonList(new ServerAddress(host, port))))
                        .build());
        return client.getDatabase(database);
    }
}

 

์ปจํŠธ๋กค๋Ÿฌ์—๋Š” ๋”ฑ 4๊ฐœ์˜ Restful API๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

@Slf4j
@RestController
@RequestMapping("/diary/life")
@AllArgsConstructor
public class DiaryLifeController {

    private final DiaryLifeService diaryLifeService;

    @ApiOperation("์ „์ฒด ๋ชฉ๋ก ์กฐํšŒ")
    @GetMapping
    public Response<List<Post>> getPosts() {
        log.info("์ „์ฒด ๋ชฉ๋ก ์กฐํšŒ");

        return new Response<>(diaryLifeService.getPosts(null));
    }

    @ApiOperation("๋‹ค์ด์–ด๋ฆฌ ํฌ์ŠคํŒ… ์‹ ๊ทœ์ƒ์„ฑ")
    @PostMapping
    public Response<Void> savePost(@RequestBody Post post) {
        validatePostInput(post);
        diaryLifeService.createPost(post);
        return new Response<>();
    }

    @ApiOperation("๋‹ค์ด์–ด๋ฆฌ ํฌ์ŠคํŒ… ์—…๋ฐ์ดํŠธ")
    @PutMapping
    public Response<Void> updatePost(@RequestBody Post post) {
        validatePostInput(post);
        diaryLifeService.updatePost(post);
        return new Response<>();
    }

    @ApiOperation("๋‹ค์ด์–ด๋ฆฌ ํฌ์ŠคํŒ… ์‚ญ์ œ")
    @DeleteMapping
    public Response<Void> deletePost(@RequestParam String _id) {
        diaryLifeService.deletePost(_id);
        return new Response<>();
    }

    private void validatePostInput(Post post) {
        if (ObjectUtils.isEmpty(post.getTitle())) {
            throw new BadRequestException("No TITLE exists.");
        }
        if (ObjectUtils.isEmpty(post.getContent())) {
            throw new BadRequestException("No CONTENT exists.");
        }
    }

    @ExceptionHandler
    public ResponseEntity<Response<String>> badRequest(BadRequestException e) {
        log.error("Bad request.., e-msg:{}", e.getMessage(), e);
        return ResponseEntity.badRequest().body(new Response<>(e.getMessage()));
    }
}

 

์Šค์›จ๊ฑฐ๋กœ ๋ณด๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์ด์–ด๋ฆฌ API ์Šค์›จ๊ฑฐ

 

์Šค์›จ๊ฑฐ๋ฅผ ์ด์šฉํ•ด์„œ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ๋ฒˆ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

 

ํ˜„์žฌ ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ์€ ๋ชจ๋‘ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ ๊นŒ์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์—…ํ•˜๋ฉด์„œ ์‰ฝ๊ฒŒ ํ’€๋ฆฌ์ง€ ์•Š์•˜๋˜ ๋ถ€๋ถ„์€ ๋ชฝ๊ณ DB๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋‹ˆ ๋ชฝ๊ณ DB ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ด์šฉํ•œ CRUD๋งŒ๋“œ๋Š” ๋ถ€๋ถ„์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด RDS์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ObjectID ๋กœ ํ•ธ๋“ค๋ง์„ ํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ์—ˆ๊ณ , auto increment pk ์„ค์ •์„ ๋”ฐ๋กœ ํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ฒŒ์‹œ๊ธ€์˜ ๋ฒˆํ˜ธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด์•ผํ• ์ง€ ๊ณ ๋ฏผ์ด ์ข€ ๋˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ๊ฐ ๊ฒŒ์‹œ๊ธ€๋งˆ๋‹ค ์‹œํ€€์Šค๊ฐ’์„ ๋„ฃ์–ด์ค˜์•ผํ•˜๋Š”๋ฐ ๊ตฌ๊ธ€๋งํ•ด์„œ ๋‚˜์˜ค๋Š” ๊ฒƒ๋“ค์€ synchronized ๊ฐ€ ์•ˆ๋  ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์—ฌ์„œ ํ…Œ์ŠคํŠธ๋„ ์ข€ ํ•ด๋ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ๊นŒ์ง€ ๋ฐฑ์—”๋“œ์˜ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„์„ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋ก ํŠธ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์˜ฌ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.