[Healing Service - DEW Calendar - 일기 저장 기능]

1.DEW 캘린더


diary emotion weather의 약자로 dew라고 이름을 지어봤다.
일기를 작성하면, AI가 감정을 분석하고 분석된 감정을 날씨로 치환해주는 서비스를 제작하여 사용자에게 보여주는 서비스이기 때문이다.

2. 구현

2.1 controller

 /**
     * 일기 저장 API
     * @param request 사용자 요청
     * @param diaryRequest 일기 제목,본문
     * @return 응답 DTO (감정,글귀,음악)
     */
    @Operation(summary = " 일기 저장 ", description = " ")
    @PostMapping("/diary")
    public ResponseEntity<AiResponse> saveDiary(HttpServletRequest request, @RequestBody DiaryRequest diaryRequest){

        /* AI 연산 */
        AiResponse aiResponse = dewService.AiCalculation(diaryRequest);

        /* 사용자 고유번호 조회 */
        Long userNumber = jwtService.extractUserNumberFromRequest(request);

        /* 일기 최종 저장 */
        dewService.saveDiary(userNumber,diaryRequest,aiResponse);

        return ResponseEntity.ok(aiResponse);
    }

가독성을 위해, 서비스 단이라고 생각하는 계산 로직들은 최대한 이번 컨트롤러 영역에서 분리해 보았다.

2.2 AI 연산 과정

컨트롤러의 첫번째 과정인 AI연산 과정 먼저 살펴보겠다.

2.2.1 DewService 인터페이스

public interface DewService {

    /**
     * 일기 AI 연산 메서드
     * @param diaryRequest 일기(제목, 본문)
     * @return 사용자 감정
     */
    AiResponse AiCalculation(DiaryRequest diaryRequest);
.
.

2.2.2 DewServiceImpl 구현체

위 인터페이스를 실제로 구현한 구현체이다.
나중에 요구사항이 변동될 수도 있으니, 서비스 단은 인터페이스와 구현체를 분리하였다.

그렇게 되면, 컨트롤러는 어차피 인터페이스를 불러오므로 변동되는 사항이 없고, 변동된 서비스 구현체만 구현하여 갈아 끼우면 될것이기 때문에 이렇게 설계하였다.

 /**
     * 일기 저장 메서드
     * @param diaryRequest 일기 제목 + 일기 본문
     * @return AI 연산 결과(감정,날씨,힐링 메시지,힐링 뮤직)
     */
    @Override
    public AiResponse AiCalculation(DiaryRequest diaryRequest) {

        /* 일기 감정 분석 */
        String rawEmotion = chatClient.call(PromptUtils.writeDiaryRequest(diaryRequest.title(),diaryRequest.diary()));
        String emotion = TextPurificationUtil.emotionPurify(rawEmotion);

        /* 감정에 맞는 힐링 메시지 & 힐링 뮤직 추천 */
        String healingMessage = HealingMessageList.getMessageByEmotion(emotion);
        String healingMusic = HealingMusicList.getMusicByEmotion(emotion);

        /* 사용자 감정을 날씨에 매칭 */
        String weather = matchEmotionToWeather(emotion);

        return new AiResponse(emotion,weather,healingMessage, healingMusic);
    }

일기를 분석하여 감정값을 얻고,
도출된 감정값을 기반으로 힐링 메시지,힐링 뮤직, 날씨를 반환한다.
여기서 힐링 메시지와 힐링 뮤직은 사용자의 감정과 관계있는 여러개의 리스트 중 하나를 랜덤 반환한다.

2.3 사용자 고유 번호 조회

/* 사용자 고유번호 조회 */
        Long userNumber = jwtService.extractUserNumberFromRequest(request);

jwtService에서 다음 메서드를 가지고 있다.

 /**
     * 사용자 요청으로 부터 사용자 고유 번호 추출
     * @param request 사용자 요청
     * @return userNumber
     */
    public Long extractUserNumberFromRequest(HttpServletRequest request){

        String AccessToken = extractAccessToken(request);

        return extractUserNumberFromToken(AccessToken);
    }

사용자 요청으로 부터 엑세스 토큰을 추출하고,
토큰으로 부터 사용자의 고유번호를 추출한다.

토큰에서는 사용자의 고유번호를 포함시키도록 generate함수를 만들었다.
엑세스 토큰 생성 함수는 다음과 같다.

 /**
     * 엑세스 토큰 생성 메서드
     * @param email 토큰의 주체
     * @return 엑세스 토큰
     */
    public String generateAccessToken(String email,String nickname,Long userNumber) {
        return Jwts.builder()
                .setSubject(email)
                .claim("userNumber",userNumber)
                .claim("nickname", nickname)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

서브젝트는 이메일이고,
클레임 값은 userNumber 와 nickname이다.

즉,jwt 엑세스 토큰안에 저 값들이 포함되어 있으니,
추출해서 사용가능하다는 의미이다.

토큰에서 사용자 고유번호를 추출하는 섬세한 코드는 다음과 같다.

public Long extractUserNumberFromToken(String token) {
        return extractClaims_userNumber(token).get("userNumber", Long.class);
    }
    public Claims extractClaims_userNumber(String token) {
        return Jwts.parser()// JWT 파서 객체 생성
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    }

userNumber이다 보니, Long.class로 받는다.

2.4 일기 최종 저장 메서드

     /* 일기 최종 저장 */
        dewService.saveDiary(userNumber,diaryRequest,aiResponse);

2.4.1 서비스 단 인터페이스

 /**
     * 일기 최종 저장 메서드
     * @param userNumber 사용자 고유 번호
     * @param diaryRequest 일기 제목 + 본문
     * @param aiResponse AI 연산결과
     */
    void saveDiary(Long userNumber,DiaryRequest diaryRequest,AiResponse aiResponse);

2.4.2 서비스 단 구현체

 /**
     * 일기 최종 저장 메서드
     * @param userNumber 사용자 고유 번호
     * @param diaryRequest 일기 제목 + 본문
     * @param aiResponse AI 연산결과
     */
    @Override
    public void saveDiary(Long userNumber, DiaryRequest diaryRequest, AiResponse aiResponse) {

        FullDiaryRequest fullDiaryRequest = new FullDiaryRequest(
                null,
                userNumber,
                diaryRequest.title(),
                diaryRequest.diary(),
                aiResponse.emotion(),
                aiResponse.weather(),
                LocalDate.now(),
                aiResponse.healingMessage(),
                aiResponse.healingMusic()
        );

        Diary diary = fullDiaryRequest.toEntity();

        dewMapper.saveDiary(diary);

    }

diary 객체를 만들어서 Mapper로 저장한다.

2.4.3 Mapper

매퍼 인터페이스

/**
     * 일기 저장 메서드
     * @param diary 일기 객체
     */
    void saveDiary(Diary diary);

XML 코드

 <!-- 일기 결과 저장 -->
    <insert id="saveDiary" useGeneratedKeys="true" keyProperty="diaryNumber">
        INSERT INTO diary (date,userNumber,title,diary,emotion,weather,healingMessage,healingMusic)
        VALUES (NOW(), #{userNumber}, #{title}, #{diary},
                #{emotion}, #{weather}, #{healingMessage}, #{healingMusic})
    </insert>

3. 결과

일기를 저장하니 성공적으로 감정,날씨, 힐링 메시지,힐링 뮤직 링크가 사용자에게 반환되었고

나머지 diary 정보들 또한 DB에 정상적으로 저장이 되었다.

profile
내가 있는 그 조직에서 가장 성실하기만 하자

0개의 댓글