[side-project/personal-blog] 3. 메시지 CRUD 서버 - Spring Boot+Docker 환경 MongoDB 구축

최지수·2022년 8월 28일
0

personal-blog

목록 보기
4/5

프로젝트를 시작하기에 앞서...

스프링 부트로 개인 블로그 서버를 만들기에 앞서, 기본적인 사용법에 익숙해지기 위해 메시지를 데이터베이스에 생성create하고 조회read, 업데이트update 그리고 삭제delete하는 API를 만들어보기로 했어요.

MongoDB를 먼저 사용할 수 있게 해보죠

pom.xmldependency를 추가해줍니다.

이를 통해 소스 코드에 mongoDB 관련 라이브러리를 가져와 사용할 수 있어요.

이런다고 끝이 아니에요. 당연히 mongoDB를 설치해야 겠지만, 사전 작업을 거친 후에 알려드릴게요.

먼저 mongoDB를 docker 환경에서 실행해봅시다

저는 docker를 설치한 상태고 여기에 다루기엔 내용이 크기 때문에 스킵할게요. 기회가 된다면 정리할게요!

MongoDB docker 실행

docker run -p 27017:27017 --name mongo_boot -d mongo

포트 번호를 27017로 한 이유는 mongoDB의 디폴트 포트 번호기 때문이에요.

그래요, 따로 설정을 하지 않고 바로 실행하고 싶었어요.

mongodocker에서 기본적으로 제공되는 이미지이므로 공식 리포짓토리에서 가져올 수 있어요.

💡 이미 포트 번호를 사용 중인 프로세스가 있다면?

  • 사용중인 프로세스 확인 : sudo lsof -i :{포트 번호}
  • 프로세스 종료 : sudo kill -9 PID

구동 여부 확인

docker exec -i -t mongo_boot bash

이후 정상적으로 docker 환경에 접속한 것을 확인하시면 정상적으로 구동된 것을 아실 수 있어요.

그 다음, 메시지와 관련된 코드를 작성해 봅시다

작성한 코드 순으로 설명해볼게요.

Message

Domain 영역에 속하고 말 그대로 메시지를 담당하는 클래스에요.

@Document(collection = "messages")
public class Message {
    @Id @Getter @Setter
    private String id;
    @Getter @Setter
    private String time;
    @Getter @Setter
    private String message;

    @Override
    public String toString() {
        return "[" + this.time + "] " + this.message;
    }
}

여기서 @Document는 이 클래스가 mongoDB에서 문서로서 매핑될 것을 의미해요.

@Document 인수 안의 collection은 데이터베이스 테이블로서의 역할을 해요.

@Getter, @Setterlombok 라이브러리의 유용한 기능 중 하나로, 자동으로 getter와 setter를 만들어줘요(ex. 변수 명 id \to getId(), setId() 메서드 생성)

Repository

그리고 MongoDB를 담당 하는 인터페이스를 아래처럼 추가하세요.

단순히 메시지 주고 받는 기능만 존재하니 따로 메서드는 선언하지 않았어요(단순히 상속하는 것만으로 데이터베이스 연동이 가능하다니 신기하네요 ㅎㅎ. 물론 이것 말고도 해야할게 있긴 해요).

import org.springframework.data.mongodb.repository.MongoRepository;

import dev.everythinglikeanoob.personalblog.domain.Message;

public interface MessageRepository extends MongoRepository<Message, String> {
    
}

Service

스프링 부트의 근간이 되는 MVC 패턴에서 대응되는 디자인이 없어 다소 헷갈렸어요.

ServiceRepository를 주입해 Controller에서 요청을 받아, 이를 기반으로 Repository에서 데이터를 CRUD를 하는 책임이 있어요.

public class MessageService {
    private final MessageRepository messageRepository;

    public MessageService(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }

    public String createMessage(String msg) {
        Message message = new Message();
        ZonedDateTime now = ZonedDateTime.now();
        message.setTime(now.toString());
        message.setMessage(msg);

        messageRepository.save(message);

        return message.getId();
    }

    public String getMessage() {
        List<Message> messages =  messageRepository.findAll();
        if(messages.isEmpty()){
            return "";
        }
        return messages.get(0).toString();
    }

    public void updateMessage(String message) {
        messageRepository.deleteAll();
        createMessage(message);
    }

    public void deleteMessage() {
        messageRepository.deleteAll();
    }
}

Controller

http 메서드 요청을 처리하는 부분이에요.

@RestController
public class MessageController {
    private final MessageService messageService;

    @Autowired
    public MessageController(MessageService messageService){
        this.messageService = messageService;
    }

    @GetMapping("/")
    public String home() {
        return messageService.getMessage();
    }

    @PostMapping("/create")
    public String create(@RequestParam(value = "message", defaultValue = "") String message) {
        if(message.isEmpty()) {
            return "Input a message!";
        }

        String messageInDB = messageService.getMessage();
        if(false == messageInDB.isEmpty()){
            return "Already exist!";
        }
        
        return messageService.createMessage(message);
    }

    @GetMapping("/read")
    public String read() {
        return "redirect:/";
    }

    @PutMapping("/update")
    public String update(@RequestParam(value = "message", defaultValue = "") String message){
        messageService.deleteMessage();
        return messageService.createMessage(message);
    }

    @DeleteMapping("/delete")
    public String delete() {
        messageService.deleteMessage();
        return "";
    }

}

REST API 디자인 가이드에 따라 CRUDhttp 메서드에 맞췄어요.

여기까지하면 요청을 처리하고 응답하는 서버 로직을 다 만든거에요. 이제 하나 남았어요.

SpringConfig

위에 있는 소스 코드만으론 스프링 부트가 데이터베이스를 인식할 수 없어요. 따라서 아래와 같이 스프링 부트가 이 데이터베이스는 실제로 존재하는 객체임을 알려 사용할 수 있게끔 해요.


@Configuration
public class SpringConfig {

    private final MessageRepository messageRepository;
    
    @Autowired
    public SpringConfig(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }

    @Bean
    public MessageService messageService() {
        return new MessageService(messageRepository);
    }    
}

@Configuration을 통해 해당 클래스는 설정을 위한 클래스임을 알려요.

@Bean은 개발자가 직접 제어가 불가능한 외부 라이브러리 등을 등록할 때 사용해요. 그리고 @Configuration은 해당 클래스에서 1개 이상의 bean을 생성하고 있음을 명시하죠.

@Autowired의 경우 해당 클래스의 인스턴스를 명시하는 것을 의미해요.

같은 타입의 bean이 2개 이상 존재할 경우, 어떤 bean을 주입해야할지 알 수 없기 때문에 스프링 컨테이너를 초기화하는 과정에서 예외가 발생해요.

이 때 @Qualifier@Autowired를 통해 어떤 bean을 사용할 수 있을지 명시할 수 있어요.

추가적으로 @Autowired를 사용하면 순환 참조가 발생하기 때문에 위처럼 생성자 주입 방식을 권장해요.

메시지를 날려봅시다

이걸 맞춰 요청할 수 있게 Talend API 크롬 확장 프로그램을 통해 보내볼게요.

메시지를 보면 정상적으로 보내졌음을 확인할 수 있어요.

POST 메서드를 통해 메시지를 보내볼게요.

정상적으로 추가되었고, 다시 조회하면,

이렇게 메시지 서버를 구성 완료했어요 ㅎㅎ.

참고

xzio님 블로그 - 스프링 부트 몽고DB(Mongo DB) 연동하기
taelee님 블로그 - 현재 열려있는 포트 확인 및 닫기(mac)
wake-up-neo.net - @Bean과 @Autowired의 차이점
여기 저번에 왔던 것 같은데? - @Component 와 @Bean, @Autowired 어노테이션 알아보기

profile
#행복 #도전 #지속성

0개의 댓글