728x90

Auto Scaling 적용 후 아키텍처 도식도

 

Auto Scaling이 필요한 이유

오토스케일링(Auto Scaling)은 클라우드의 유연성을 돋보이게 하는 핵심기술로 CPU, 메모리, 디스크, 네트워크 트래픽과 같은 시스템 자원들의 메트릭(Metric) 값을 모니터링하여 서버 사이즈를 자동으로 조절한다. 이를 통해 사용자는 예상치 못한 서비스 부하에 효과적으로 대응하고 비용 절감 효과를 볼 수 있다.

 

Auto Scaling 적용

Auto Scaling 서비스는 Launch Configuration을 생성하고, Auto Scaling Group을 생성한 후에 이용할 수 있다.

 

Launch Configuration 생성

1. Launch Configuration 생성

image

2. 서버 이미지 설정

image


나는 centos-7.8-64로 선택하였다.

 

3. 서버 설정
서버 이미지를 설정 하고 서버를 설정한다.

image


서버 타입을 설정하고, 서버에 init Script가 설정 되어 있는 서버에서만 SourceDeploy 서비스를 이용할 수 있다.

init Script는 다음과 같다.

image


참고: 링크

 

4. 서버 이름 설정

image


서버의 이름을 설정한다.

 

5. 서버 인증키 설정

image


인증키를 설정한다. NCP에서 발급 받은 .pem 파일로 인증을 한다. Auto Scaling이 생성 해내는 인스턴스에 접근할 때 사용된다.

이 설정이 마치면 Auto Scaling Launcher가 생성된다.

 

Auto Scaling Group 생성

1. Auto Scaling Group 생성

image

2. Launch Configuration 선택
Launch Configuration을 선택한다.

image

3. Auto Scaling Group 설정

image


오토스케일링을 생성하기 전에 오토스케일링 전용 Subnet을 만들어서 적용시켜준다.

입력사항 설명
최소 용량 그룹의 최소 서버 수
최대 용량 그룹에서 생성 가능한 최대 서버 수
기대 용량 서버의 수는 기대 용량값에 따라서 조정된다. 이 값은 최소 용량 이상, 최대 용량 이하여야 한다.
쿨다운 기본값(초) 실제 Scaling이 수행 중이거나 수행 완료된 이후에 모니터링 이벤트 알람이 발생하더라도 반응하지 않고 무시하도록 설정한 기간이다. 기본값은 300초이다.
헬스 체크 보류 기간 서버 인스턴스가 생성되어 상태가 ‘운영 중’으로 변했더라도, 서버의 업데이트 설치 등 작업에 의해서 헬스 체크에 정상 응답하지 못하는 경우가 생길 수 있다. 이런 경우 헬스 체크 보류 기간을 지정하면 해당 기간 동안에는 헬스 체크에 실패하더라도 서버 헬스에 이상이 있다고 판단하지 않는다.
헬스 체크 유형 헬스체크 유형이다. 서버, 로드벨런서가 있다.

 

4. 네트워크 접근 설정
ACG를 생성하여 적용시킨다.

5. 정책 설정

image

Scaling 정책은 3가지 방식으로 설정할 수 있다.

(1) 증감변경: 현재 그룹 크기와 상관없이 지정한 서버 대수를 직접 추가 또는 삭제하는 방법

(2) 비율변경: 현재 그룹 크기 대비 일정한 비율(%)로 서버를 증감시키는 방법.

(3) 고정값: 그룹 크기를 지정한 값으로 고정시키는 방법.

쿨다운(cooldown)이란 일단 Scaling이 수행되면 추가적인 알람이 발생하더라도 이에 반응하지 않도록 설정된 시간이다. 쿨다운 시간 동안은 Scaling의 수행이나 완료 여부에 상관없이 추가로 발생한 이벤트에 대해서 아무 동작을 하지 않게 된다.

그 다음에는 오토스케일링 작동 시 문자, 이메일 발송하는 설정을 하고 생성이 완료된다.

 

Auto Scaling 적용 결과

image

 

728x90

CI/CD 적용 후 아키텍처 도식도

 

CI/CD가 필요한 이유

  • 새로운 코드 변경사항이 정기적으로 테스트 빌드 되므로, 여러 명의 개발자가 동시에 어플리케이션 개발을 할 때 충돌 문제를 해결할 수 있다. 그리고 개발자의 변경 사항을 리포지토리에서 고객이 사용 가능한 프로덕션 환경까지 자동으로 릴리스하기 위함이다.
  • CI/CD 프로세스를 적용함으로써 수작업으로 진행되었던 많은 부수적인 일들을 줄일 수 있다. 그리하여 개발자는 개발에 집중할 수 있다.

 

CI/CD 적용

NBP(네이버 클라우드 플랫폼)에는 CI/CD 구축을 돕기 위해 SourceCommit, SourceBuild, SourceDeploy, SourcePipeline을 제공하고 있다. 나열한 순서대로 CI 부터 CD까지 적용을 돕는다. 그리고 SourcePipeline으로 CI/CD 모든 과정을 이어준다.

SourceCommit

1. 외부 레포지토리 복사를 클릭

image

"레포지토리 생성"을 누르면 빈 레포지토리가 생성되고 "외부 레포지토리 복사"를 누르면 외부의 레포지토리를 복사할 수 있다. 나는 깃헙에 있는 레포지토리를 복사하는 것을 선택했다.

 

2. 레포지토리 생성

image

레포지토리 이름과 복사할 Git URL을 입력한다.

그 다음 File Safer 상품과 연동할 것인지 묻는다. (File Safer는 고객이 업로드/다운로드 하는 파일의 악성코드 여부를 검사할 수 있는 서비스이다.)

처음에 연동을 하고 하루가 지나고 지불된 금액을 확인했는데 바로 10만원 가까이 나가있었다.. 그래서 나는 연동을 하지 않았다.

 

3. 생성

image

확인을 누르면 생성이 된다.

 

4. 생성된 레포지토리를 클릭하고 코드 이동을 누르면

imageimage

깃헙에 있었던 코드들이 그대로 옮겨져 있는 것을 볼 수 있다. 여기서 오해할 수 있는 점이 깃에 푸시를 해도 NCP의 SourceCommit 레포지토리에 반영은 되지 않는다. 그 의미는 깃헙의 깃액션 CI를 사용하지 못한다는 의미이다.

해당 SourceCommit 레포지토리에 반영을 하려면

image

따로 git clone을 해줘야한다. SourceCommit은 git 명령어를 지원해서 기존 깃헙 사용자들도 사용하기 편리할 것이다.

 

SourceBuild

1. 빌드 프로젝트 생성

image

2. Object Storage 연결

image

빌드한 결과물을 저장해두는 저장소 같은 개념이다. AWS의 S3랑 비슷한 개념을 가진 저장소이다.

 

3. 빌드 프로젝트 생성 - 기본 설정

image

여기서 중요한 곳은 빌드 대상인데, SourceCommit, 깃헙, Bitbucket에 있는 레포지토리 세 가지를 선택할 수 있다. 나는 SourceCommit을 선택하였다.

깃헙을 선택해서 깃헙에 있는 레포지토리에 push를 하면 그게 트리거가 되어서 배포 파이프라인이 작동되게끔 하고 싶었는데 NCP에서는 깃헙의 트리거를 못받는 것 같았다. 그래서 빌드 대상을 깃헙 대신 SourceCommit을 선택 하였다.

이 부분은 추후 젠킨스로 구현 해봐도 좋을 것 같다.

 

4. 빌드 프로젝트 생성 - 빌드 환경

빌드 환경 설정이다.

image

ubuntu 16.04 (x64) 환경에서 java로 빌드하게 하였다. 비용 문제로 컴퓨팅 유형은 제일 작은걸로 하였다.

그리고 나의 프로젝트에서 docker-compose를 사용하기 때문에 도커 환경이 필요했다. 그래서 도커 이미지 빌드 를 체크 해주었다. 도커 환경이 필요하지만 체크를 하지 않으면 명령어로 도커를 다운로드하고 설정하는 귀찮은 작업을 따로 해주어야한다.

 

5. 빌드 프로젝트 생성 - 빌드 명령어 설정

image

SourceCommit에 있는 프로젝트를 빌드 하고 필요한 명령어를 입력하는 곳이다.

나는 빌드 후 프로젝트 전체를 jar파일로 말아올려 서버에서 실행시킬 명령어인 ./gradlew bootjar 명령어를 입력 해주었고 깃헙을 거쳐 깃헙 액션을 실행시키지 못하는 환경을 대신해서 ./gradlew test 명령어를 실행시켜 빌드 후 테스트 과정도 거치게 하였다.

 

6. 빌드 프로젝트 생성 - 결과물 업로드 설정

image

빌드 후 결과물을 Object Storage에 저장할 때 경로를 설정하는 곳이다. 빌드 결과물은 .zip 파일로 저장이 된다. 빌드 결과물을 저장할 Storage를 설정하고 경로, 폴더/파일명을 설정한다. 그리고 백업파일 여부도 설정한다.

백업 사용을 설정하면 Object Storage에 따로 백업 폴더가 생성된다.

 

7. 빌드 프로젝트 생성 - 추가 상품 연동

image

추가 상품은 비용 문제로 연동하지 않았다.

 

SourceDeploy

1. 배포 프로젝트 생성

image

2. 프로젝트 이름 설정

image

3. 배포 환경 설정

image

배포 환경 설정에서 stage를 설정하고 배포 타겟을 설정한다. 나는 Auto Scaling 되고있는 서버 전체에 모두 배포를 하기 위해서 배포 타겟을 Auto Scaling으로 했다. 사실 Server를 체크 해서 해도 되지만 만약 Auto Scaling에서 서버를 자동적으로 늘릴 때 그 서버를 수동으로 포함 시켜줘야하기 때문에 Auto Scaling으로 체크 하였다.

 

4. 배포 시나리오 설정

설정이 끝나면 배포 시나리오를 설정 해야한다.

image

아래처럼 배포 시나리오를 설정하면 된다.

image

배포 전략에 지금은 기본으로 되어있지만 추후 블루/그린 배포 전략을 사용 해볼 예정이다.

배포 과정은 순차 배포, 동시 배포가 있는데 순차 배포가 더 낫다고 생각하여 순차배포를 선택하였다. 그 이유는 배포과정에 놓여진 서버는 로드벨런서가 트래픽을 보낼 수 없기 때문에 순차적으로 배포 될 때 배포 되고 있지 않는 서버들에게는 트래픽을 보낼 수 있기 때문에 무중단 배포가 된다고 생각하여 순차 배포가 낫다고 생각하였다.

배포 파일은 Object Storage에 있는 빌드된 zip 파일을 배포하는 것으로 설정 해두었다.

그 다음은 배포 명령어를 설정한다.

image

배포 전 실행, 파일 배포, 배포 후 실행으로 나뉜다.

설정할 때 주의할 점이 경로이다. 그리고 한 줄이 실행되면 원래의 경로로 돌아오기 때문에 &&로 구분하여 묶여있는 명령어는 한번에 처리해야한다.

 

배포 전 실행

구분 실행 명령어 설명
1 cd /usr/lib && wget https://download.java.net/java/GA/jdk16.0.2/d4a915d82b4c4fbb9bde534da945d746/7/GPL/openjdk-16.0.2_linux-x64_bin.tar.gz /usr/lib 경로로 가서 자바 다른 버전을 다운 받는다. (다른 버전을 받는 이유가 NCP가 지원하는 자바 버전이 최고 11버전이기 때문에 16버전을 다운 받기 위함)
2 tar -zxvf /usr/lib/openjdk-16.0.2_linux-x64_bin.tar.gz -C /usr/lib 위에서 받은 .tar 파일을 /usr/lib에 압축 풀기
3 yum install -y yum-utils yum-utils 를 최신 버전으로 업데이트를 한다.
4 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo Docker Engine을 설치할 수 있도록 저장소를 추가한다.
5 yum install docker-ce docker-ce-cli http://containerd.io/ -y 도커 엔진 최신 버전을 설치한다.
6 curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 도커 컴포즈를 설치한다.
7 chmod +x /usr/local/bin/docker-compose 도커 컴포즈가 실행할 수 있게 권한을 부여 한다.

 

파일 배포

구분 소스 파일 경로 배포 경로 설명
1 skka.zip/ /root/deploy skka.zip 파일을 /root/deploy 경로에 배포한다.

 

배포 후 실행

구분 실행 명령어 설명
1 systemctl start docker 도커 엔진 수동으로 데몬 실행
2 systemctl enable docker 도커 서비스 등록하여 부팅시 실행 하도록 하기
3 /usr/local/bin/docker-compose -f /root/deploy/docker-compose.yml up -d /usr/local/bin/docker-compose 로 도커 컴포즈로docker-compose.yml 파일에 설정 되어 있는 도커 이미지 띄우기
4 nohup /usr/lib/jdk-16.0.2/bin/java -jar /root/deploy/build/libs/skka-0.0.1-SNAPSHOT.jar & bootjar로 말아 올린 jar 파일 백그라운드로 실행

 

SourcePipeline

1. 파이프라인 생성

image

2. 파이프라인 이름 설정

image

3. 파이프라인 구성

image

작업 추가를 눌러서 파이프라인을 구성해야 한다.

그리고 Trigger를 설정한다. 트리거가 설정되면 설정된 트리거가 발생하면 설정된 파이프라인이 실행된다. 나는 SourceCommit에 push가 되면 트리거가 발동되어 설정 되어 있던 파이프라인인 빌드, 배포가 순차적으로 실행된다.

다음은 설정된 화면이다.

image

 

728x90

문제

<choose>
    <when test = ”isLiked == ‘t’”>
        UPDATE t_book_like
        SET is_liked = 'f'
        WHERE identity_id = #{userId} AND book_id = #{bookId}
    </when>

<when test = ”isLiked == ‘t’”> 여기서 난 문제였다.

 

 

해결

<when test='isLiked == "t"'> 이렇게 ‘ ‘ 로 해주니까 해결 되었다.

728x90

깃헙 액션 스크립트는 별로 특별한게 없다.. 별로 달라진것도 없고.. 

name: PR test CI

# 하기 내용에 해당하는 이벤트 발생 시 github action 동작
on:
  push: # feature/*와 pre-production와 main 브랜치에서 push가 일어났을 때 github action 동작
    branches:
      - 'feature/*'
      - 'main'
      - 'pre-production'
  pull_request: # feature/*와 pre-production와 main 브랜치에서 PR이 일어났을 때 github action 동작
    branches:
      - 'feature/*'
      - 'main'
      - 'pre-production'

# 참고사항
# push가 일어난 브랜치에 PR이 존재하면, push에 대한 이벤트와 PR에 대한 이벤트 모두 발생합니다.

jobs:
  build: 
    runs-on: ubuntu-latest # 실행 환경 지정

    steps:
      - uses: actions/checkout@v2 # github action 버전 지정(major version)

      - name: Set up JDK 11 # JAVA 버전 지정
        uses: actions/setup-java@v3
        with:
          java-version: 11
          distribution: 'temurin'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Build with Gradle # 실제 application build(-x 옵션을 통해 test는 제외)
        run: ./gradlew build
#         run: ./gradlew build -x test
        
#       - name: Test with Gradle # test application build
#         run: ./gradlew test

      - name: Publish Unit Test Results # test 후 result를 보기 위해 추가
        uses: EnricoMi/publish-unit-test-result-action@v2
        if: ${{ always() }} # test가 실패해도 report를 남기기 위해 설정
        with:
          files: build/test-results/**/*.xml

 

 

그런데....

 

 

 

이 화면의 연속이었다. 어제 오늘...순수 해결하려고 노력한 시간만 대략 20시간 동안 꼬박 이것만 한 것 같다.

 

DoseoroApplicationTests > contextLoads() FAILED
 java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
 

이 오류에 대한 원인을 살펴봤었다.

  • MySql의 문제
  • DB가 물리지 않았기 때문
    • 디비가 물렸어도 테이블이 없기 때문
      • 현재 MyBatis를 사용하고 있는데 JPA나 Node js에 Sequelize 같은 ORM은 자동으로 테이블을 만들어 주지만 MyBatis에는 그런게 없는데 어떡하지? 의 문제

등등 긴 시간동안 많은 고민이 있었던 것 같다...

 

 

1.  처음에 MySql의 문제로 다가갔다.

테스트 코드가 돌려면 어쨌든 DB에 갔다 와야하는데 깃헙 액션 스크립트를 봤는데 MySql을 세팅하는 로직이 없었다. 그래서 

 

 - name: Setup MySQL
        uses: samin/mysql-action@v1
        with:
          mysql user: 'root'
          mysql password: ${{ secrets.MYSQL_PASSWORD }}

 

이 코드를 추가했다. 그런데 예상 외로 실행되지  않았다... 그렇다면 무슨 문제가 있을까 하며 다른 문제를 찾았다. MySql을 세팅을 했지만 거기에 테이블이 없네??? 그러면 만들어야지. 라며 생각했다. 그런데 H2가 요즘 현업에서 테스트용도로만 쓴다고 했다.

 

2.  H2의 적용

그래서 찾아보니 H2는 정말정말 구미가 당길만한 능력이 있는 친구였다. In-Memory 형식 디비를 구축할 수 있었다. H2 DB를 메인 메모리(JVM) 위에서 돌아가며 애플리케이션이 종료되면 없어지는 휘발성 디비였다. 그러면 완전 좋은거네??? 이득!! 하면서 바로 적용해보기로 했다. 그리고 메인 메모리에서 돌아가는 DB라 데이터 캐싱 DB로도 사용할 수 있는 장점을 느꼈다. 다시 사용할거라는 기약을 했다.

 

테스팅 용으로 H2를 적용은 했는데 이제 어떻게해?? 라는 의문점만 남았다. 그냥 돌려봐야지. 라는 생각만 들었다. 빨리 결과를 도출하고싶은 급한 마음이었다... 당연히 안될게 뻔했다. 내 자신이 부끄러울정도로 멍청하였다. 테이블이 없는데 어떻게 돌아가겠니!!!... 바로 테이블을 생성하는 시도로 넘어가자.

 

3.  테이블 생성

지금까지 MyBatis를 사용하면서 코드상으로 DDL을 한번도 한적이 없었다. 어떻게 하지..? 라는 생각이 들고 계속 검색만 했다. 정말 여러 방법들이 있었다. 이번에 자바 스프링을 처음 배우면서 MyBatis만 사용해봤으니 다른 것도 사용해볼까? 라는 생각이 들었다. JdbcTemplate이 pure 한 쿼리문을 넣을 수 있고 예전부터 사용해보고 싶었는데 이번 기회에 사용해보면 좋겠다 싶어서 사용해봤다. 정말정말 편했다...

 

JdbcTemplate으로 테이블을 생성하고 테스트코드 로직에 맞게 잘 수정한 뒤 로컬에서 테스트를 돌려보았다. 잘 돌아갔다. 그러면 깃헙 액션에서도 잘 돌아가겠지?? 라는 안일한 생각으로 기쁜 마음으로 돌려보았다. 결과는 아직이었다.. 계속 위와 같은 오류를 뿜어냈다. 그래도 고지가 코앞이라는 생각이 들었다. 이 문제를 해결할 자신이 있었다. 

 

4. application.yml 의 분리

테스트 용도랑 로컬용도로 분리하였다. main과 test 폴더 각각 resource 폴더가 있고 그 안에는 application.yml 파일이 각각 있었다. 그런데 main에는 application-test.yml을 추가하고 

spring:
  profiles:
    active: test

을 추가하며 test 용도로 분리했다.  그리고 test 폴더의 application.yml은 

spring:
  profiles:
    active: test
  config:
    import: application-test.yml

main의 application.yml을 불러오는 식으로 처리했다. 이제 완벽히 세팅이 끝났다고 생각하고 깃헙 액션을 돌려보았다. 잘 안됐다. 다 됐다고 생각했는데 왜 안되지???

 

+ ./bin에 있는 H2 라이브러리를 추가할 때 바로 위와 같은 상황일 때 main에만 추가 해주면 된다.

 

5. 커밋 히스토리를 보고 .gitignore를 확인하는 순간 해결

커밋 히스토리를 유심히 보니 테스트 폴더쪽에 application.yml이 들어가있지 않았다 .gitignore에 application.yml이 추가가 되어있었다. 정말 바보같은 실수였다. 테스트 폴더쪽의 application.yml과 함께 푸시를 하면서 모든 문제는 해결이 되었다. 안도의 한숨을 쉰다.

728x90

 

MySql 테이블에 컬럼 타입이 JSON인 컬럼을 MyBatis로 읽어오려고 했는데 처음에 오류가 나서 12시간 가량 삽질 끝에 빛이 보였다. 이 과정을 공유하려고 한다.

Select

문제

MySql 테이블은 다음과 같다.

CREATE TABLE `t_book`
(
		...
		
		`title` VARCHAR(100) NOT NULL,
		`about` VARCHAR(100) NOT NULL,
		`price` VARCHAR(100) NOT NULL,
		`img`   JSON             NULL,
		
		...
}
<select id="findHomeDisplayedBooks" resultMap="HomeDisplayedBookVO">
    SELECT
        title,
        about,
        price,
        img
    FROM t_book
    ORDER BY createdAt
    DESC  LIMIT 5
</select>

이것의 결과로 이렇게 나왔었다.

[HomeDisplayedBookVO{title='나는찐개발자', about='나.', price='777', images=null}]

해결

<resultMap id="HomeDisplayedBookVO" type="com.myproject.doseoro.packages.book.vo.HomeDisplayedBookVO">
    <result property="title" column="title"></result>
    <result property="about" column="about"></result>
    <result property="price" column="price"></result>
    <result property="images" column="img" typeHandler="com.myproject.doseoro.global.util.JsonTypeHandler"></result>
</resultMap>
<select id="findHomeDisplayedBooks" resultMap="HomeDisplayedBookVO">
    SELECT
        title,
        about,
        price,
        img
    FROM t_book
    ORDER BY createdAt
    DESC  LIMIT 5
</select>

insert 처럼 typeHandler를 추가해주었더니 DB에서 JSON으로 데이터를 가져올 수 있었다.

typeHandler 코드는 다음과 같다.

 

 

Insert

문제

MySql에 테이블은 select 할때와 같고, img 컬럼을 insert 하고싶었다. 오류가 계속 났다... 시도 끝에 해결책을 알아냈다.

CREATE TABLE `t_book`
(
		...
		
		`title` VARCHAR(100) NOT NULL,
		`about` VARCHAR(100) NOT NULL,
		`price` VARCHAR(100) NOT NULL,
		`img`   JSON             NULL,
		
		...
}

 

해결

<resultMap id="BookVO" type="com.myproject.doseoro.packages.book.vo.BookVO">
        <result property="images" column="img" typeHandler="com.myproject.doseoro.global.util.JsonTypeHandler"></result>
</resultMap>
<select id="findBookByBookId" resultMap="BookVO" parameterType="String">
    SELECT
    	img,
    FROM t_book
    WHERE id = #{bookId}
</select>

이것 또한 typeHandler 를 넣었어야 했습니다. typeHandler의 코드는 위와(insert 때와) 같습니다.

 

마치며

해결하고 나니 너무 뿌듯했습니다. 고민하고 찾는 시간은 고됐지만 해결 후 뿌듯함은 더할나위 없이 기쁘네요. 

728x90

이런 오류가 나타났다.

 

이 오류는 dao.xml 에 중복되는 resultType이 있었기 때문이다. 처음에는 어차피 객체를 생성하는 틀이라고 생각해서 중복되는 VO나 DTO를 사용해도 될 것 같다고 생각했는데 아니었다. 그래서 다시 VO를 만들어주었다.

728x90

문제

Error attempting to get column 'id' from result set

이런 오류가 발생했다. 너무 어처구니 없는 오류였다.

 

해결

BEFORE

<select id="loginCheck" resultType="boolean" parameterType="String">
    SELECT
        id AS id,
        email AS email,
        name AS name,
        nick_name AS nickName,
        phone AS phone,
        forgot_pw_question AS forgotPwQuestion,
        forgot_pw_answer AS forgotPwAnswer
    FROM t_identity
    WHERE email = #{email}
</select>

 

AFTER

<select id="loginCheck" resultType="HashMap" parameterType="String">
    SELECT
        id AS id,
        email AS email,
        name AS name,
        nick_name AS nickName,
        phone AS phone,
        forgot_pw_question AS forgotPwQuestion,
        forgot_pw_answer AS forgotPwAnswer
    FROM t_identity
    WHERE email = #{email}
</select>

 

느낀 점

생각 짧았다.. mybatis를 처음 하다보니까 인터페이스에 선언하는 것과 메소드에 선언하는 것을 잠깐 혼동하고 저 sql 문에 해당하는 값이 db에 있으면 true 없으면 false로 출력한다는 생각에만 사로잡혀서 resultType 을 boolean 으로 해놓았다. 그래서 이런 에러가 생겼던 것이었다. HashMap 으로 바꾸니 정상적으로 됐다.

728x90

문제

mybatis에서 실제 sql이 있는 xml 파일이 인터페이스로 된 파일(DAO) 에서 Param 으로 들어오는 값들을 @Param("")으로 지정을 해주지 않아서 인식을 못해 바인딩이 되지 않은 것 같다.

 

해결

BEFORE

Boolean loginCheck(String email);

 

AFTER

Boolean loginCheck(@Param("email")String email);

 

느낀 점

지금까지 전 처럼 사용했는데 이런 오류가 난거 보니까 잘못 써 온것같다.

728x90

문제

attempted to return null from a method with a primitive return type (boolean)

방금 개발을 하다가 이처럼 에러가 났다. 내가 반환하려고 하는 타입은 boolean 인데 null 로 반환되었기 때문에 이러한 에러가 발생했다.

 

해결

null 값도 받을 수 있는 Wrapper Class 를 사용하면 된다.

그래서 return type인 boolean 을 Boolean 으로 바꿔주었다. 정상적으로 동작한다.

 

느낀 점

자바의 기초를 다져놓고 스프링을 하니까 뭔가 많이 편하고 더 알아가는 것과 느끼는 것이 많아서 좋다 ㅎㅎ

다시 한번 기초가 중요하다는 것을 느꼈다!

728x90
바뀌기 전

 

원래 쿼리 매핑을 해주는 xml 파일인 "DoseoroDao.xml" 이 원래 dao 폴더에 있었고 application.yml 에는 밑에와 같이 있었다.

mybatis:
  config-location: classpath:mapper.xml

 

바뀐 후

 

 

Deoseoro.xml 파일을 resources 폴더로 옮기고 밑에와 같이 추가 하니까 정상적으로 실행 되었다..

mybatis:
  config-location: classpath:mapper.xml
  mapper-locations: classpath:DoseoroDao.xml

 

느낀 점

폴더 경로를 인식하는데 문제가 있는 것 같았다. 리소스 루트소스 루트의 경로 인식이 다른 것 같다. 이를 명심해야겠다..

 

 

참조

https://twofootdog.github.io/Mybatis-Invalid-bound-statement(not-found)-%EC%97%90%EB%9F%AC/ 

 

[Mybatis]invalid bound statement (not found) 에러 | 두발로걷는개

Mybatis 에러 원인 및 해결 방법

twofootdog.github.io

 

+ Recent posts