현재 진행 중인 사이드 프로젝트에서 카카오 채널로 알림톡 보내는 기능을 맡아 개발하게 됐다.

sms 보내는 기능 관련 글들은 많은데 알림톡 관련 자료가 많지 않아 게시물로 남기기로 했다.

 

1편은 알림톡을 사용하기 위한 세팅을 다룹니다.

틀린 내용이 있다면 댓글로 지적 환영합니다!


SENS?

SENS(Simple & Easy Notification Service)는 별도의 메시지 서버 구축 없이 다양한 메시지 알람 기능을 구현할 수 있는 서비스

(출처: NCP 사이트)

카톡 알림톡 서비스를 제공해주는 biz-message가 있으며 

카톡 API보다 더 사용하기 간편하단 이점이 있다!

 

알림톡 vs 친구톡

알림톡: 해당 채널의 친구가 아니어도 전화번호를 통해 알림을 보내는 서비스

친구톡: 해당 채널의 친구에게 홍보성 알림을 보내는 서비스

 

알림톡 비용 = 건당 7.5원

 

<알림톡 세팅 과정> 

1. 카카오톡에서 카카오 채널 개설

 

2. NCP(Naver Cloud Platform) 회원 가입

 

3. Biz Message 신규 프로젝트 생성 후 서비스 id 키 확인

Simple & Easy Notification Service -> Project에서 생성 가능

https://console.ncloud.com/sens/project

생성 후 열쇠 모양 키 누르면 서비스 id 확인 가능

 

4. 카카오톡 채널 등록

Simple & Easy Notification Service -> Biz Message -> KakaoTalk Channel

https://console.ncloud.com/sens/kakao-talk-channel

 

5. 키 생성

우측 상단 내 프로필 -> 계정 설정 -> 로그인 -> 계정 관리 -> 인증 키 관리 -> 신규 키 생성 및 확인 

 

6. 알림톡 템플릿 등록 및 검수 받기

Simple & Easy Notification Service -> Biz Message -> AlimTalk Template

https://console.ncloud.com/sens/kakao-alimtalk-template

 

*여기서 알림톡 템플릿 등록 왜 하나요??

변수가 포함된 글을 알림톡 템플릿으로 미리 등록한 후 해당 템플릿의 식별 코드를 프로젝트 코드에서 입력해 알림톡을 보내는 로직입니다.

즉, 프로젝트 코드에서 알림톡의 내용을 작성하는 것이 아니라 미리 변수가 들어간 글 내용을 NCP 사이트에서 템플릿으로 등록해둔 후

변수만 프로젝트 코드에서 그때그때 끌고와서 미리 등록한 템플릿 내용과 동일한 내용으로만 알림을 보낼 수 있습니다.

템플릿 등록: 평균 2-3일의 검수 과정을 통과한 후 등록 가능하다합니다. (필자는 검수 통과 3시간 걸려서 2-3일보다 빨리 되는 것 같습니다.)

템플릿 수정: 검수 대기/요청/반려 과정에서만 수정 가능하며 카카오톡 채널 및 템플릿 코드는 변경 불가합니다.

템플릿 삭제: 검수 요청/대기/반려 상태의 템플릿만 삭제 가능합니다.

 

알림톡 템플릿 변수가 들어간 내용 예시

#{name}님, 두둥에 가입하신 것을 환영합니다!
두둥은 누구나 자유롭게 공연을 홍보하고 인원을 모집할 수 있는 서비스입니다.
호스트가 되어 밴드, 뮤지컬, 버스킹등의 공연을 열고 홍보하세요

변수 꼴 : #{변수명]

 

템플릿 작성 가이드

https://kakaobusiness.gitbook.io/main/ad/bizmessage/notice-friend/content-guide

 

알림톡 제작가이드 - kakao business 비즈니스 가이드

광고성 요소와 동시에 사용 가능하며, 부가정보 500자, 광고성 80자로 각각 제한되며, 본문과 합쳐 총 1,000자를 넘을 수 없습니다.

kakaobusiness.gitbook.io

템플릿 등록 가이드

https://guide.ncloud-docs.com/docs/ko/sens-sens-1-5

 

웹 콘솔 사용 가이드

 

guide.ncloud-docs.com

 

7. 발급 받은 키들 프로젝트에 넣기

yml 파일, .env 파일 등 프로젝트에서 키를 관리하는 곳에 키들을 넣어 준비해주세요.

access 키, secret 키, service id가 필요합니다.

변수명은 원하는대로 지정하셔도 됩니다.

NCP_ACCESS_KEY=dfdj~~~~~~
NCP_SECRET_KEY=LkhU0P~~~~
NCP_SERVICE_ID=ncp:~~~~

 

참고 사이트

https://guide.ncloud-docs.com/docs/ko/sens-sens-1-5

https://honeystorage.tistory.com/188

https://kakaobusiness.gitbook.io/main/ad/bizmessage/notice-friend/content-guide

진행 중인 프로젝트에서 서버 성능을 향상 시키기위해 커넥션 풀을 늘리는 작업을 진행했다.

관련해 공부한 내용을 정리해봤다.


서버 성능을 향상시킬 때 중요한 지표는 처리량응답시간이다.

응답시간이 짧을수록 처리량이 높아질수록 성능이 좋아진다.

처리량을 높이는 방법 중에 커넥션 풀을 늘리는 방법이 있는데 오늘은 이에 대해 포스팅 해보려고한다.

 

DB connection?

WAS는 http 요청에 따라 쓰레드를 생성해 비즈니스 로직을 수행한다.

이때 비즈니스 로직 수행을 위해 DB 서버로부터 데이터를 얻어와야하는데

DB 접속을 위해 드라이버를 로드한 후 디비 커넥션 객체를 생성해 물리적으로 DB 서버에 접근하게된다.

자바는 주로 jdbc를 활용한다.

 

connection pool이란?

WAS가 실행될 때 미리 설정된 일정량만큼의 디비 커넥션된 객체들을 만들어 풀에 저장해둔다.

클라이언트 요청이 올 경우 이 디비 커넥션 객체를 빌려주었다가 요청이 완료되면 다시 반납받아 풀에 저장해둔다.

커넥션 풀이 커지면 메모리 소모가 커지지만 동시성이 좋아져 클라이언트 요청의 대기 시간이 감소한다.

 

왜 미리 DB connection 객체를 준비해둘까?

서버의 부하를 줄일 수 있다.

-> DB 커넥션을 맺는 과정의 부하가 크다. 미리 생성하고 재활용하는 방식을 통해 부하를 줄일 수 있다.

 

왜 미리 설정한 일정량 만큼만 DB connection 객체를 만들까? 많을수록 좋은 거 아닐까?

-서버는 한정적인 자원이기 때문에 디비 커넥션 수를 제한해야지만 서버 자원 고갈을 방지할 수 있다.

-커넥션을 활용하는 주체는 쓰레드로 커넥션이 많아도 쓰레드의 수가 뒷받침되지 않으면 의미없다.

->활용되는 커넥션이 많다는 의미는 쓰레드를 많이 사용한다는 의미로 이는 곧 context switching이 많이 일어나 오버헤드가 많이 발생한다는 의미이며 성능적인 한계가 존재하게 된다.

 

connection pool 설정 값

initialSize

:커넥션 풀 생성 시 최소 생성한 connection 객체의 수

minIdle

:최소한으로 유지할 connection 객체 수

maxIdle

:반납된 유휴 connection 유지할 최대 객체 수

maxActive

:동시 사용가능한 최대 connection 객체 수

 

maxActive>=initialSize

maxIdle>=minIdle 이어야하며

maxIdle = maxActive 이어야 좋다.

 

적절한 connection pool 크기

Thread Pool 크기 < Connection Pool 크기

 

적절한 connection 크기 = ((core_count) * 2) + effective_spindle_count)

core_count: 현재 사용하는 서버 환경에서의 CPU 개수

effective_spindle_count: DB 서버가 관리할 수 있는 동시 I/O 요청 수

 

RDS max_connections 디폴트 값: DBInstanceClassMemory/12582880

=>RDS 서버의 종류에 따라 다르다.

 

MySQL max_connections 현재 값 확인 명령어

SHOW GLOBAL VARIABLES LIKE 'max_connections';

 

HikariCP

스프링 부트 2.0부터 default JDBC connection pool

application.properties에서 간단하게 HikariCP의 설정 가능

 

스프링부트 yml 파일 HikariCP 설정 예시코드

spring:
 datasource:
   url: 주소주소
   username: root
   password: Password
   hikari:
     maximum-pool-size: 100 #최대 pool 크기
     minimum-idle: 10 #최소 pool 크기
     idle-timeout: 600000 #연결위한 최대 유후 시간
     max-lifetime: 1800000 #반납된 커넥션의 최대 수명

 

 

참고 사이트

https://aws.amazon.com/ko/premiumsupport/knowledge-center/rds-mysql-max-connections/

https://linked2ev.github.io/spring/2019/08/14/Spring-3-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%B4%EB%9E%80/

https://steady-coding.tistory.com/564

https://programmer93.tistory.com/74

https://juinthyme.tistory.com/70

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=dnjsjd11&logNo=221270990625 

https://velog.io/@miot2j/Spring-DB%EC%BB%A4%EB%84%A5%EC%85%98%ED%92%80%EA%B3%BC-Hikari-CP-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

    public UserDevice findLatestDevice (User user){
        return jpaQueryFactory.selectFrom(userDevice)
                .where(userDevice.id.eq(
                        JPAExpressions.select(userDevice.id.max())
                                .from(userDevice)
                                .where(userDevice.user.id.eq(user.getId()))
                                .where(userDevice.deviceCategory.name.eq("핸드폰"))
                ))
                .fetchFirst();
    }

인자로 받은 유저의 핸드폰이라는 이름의 유저기기 정보 테이블들의 행 중 가장 id 값이 큰 엔티티 반환

진행 중인 프로젝트 서버가 내일 더 큰 서버로 확장 이전되며 S3 관련 설정을 바꿔주는 작업이 필요해졌다!

내일 관련 회의에 참여해 작업을 진행하기 전에 미리 S3에 관해 정리를 해가면 좋을 것 같아 공부할 겸 포스팅을 하게됐다.

 

Amazon Simple Storage Service(Amazon S3)란?

AWS에서 제공하는 파일 서버의 역할을 하는 객체 스토리지 서비스

일반적인 파일 서버는 트래픽이 증가함에 따라 장비를 증설하는 작업이 필요한데 S3가 이를 대행해줘 트래픽에 따른 시스템적인 문제를 해결해준다.

Amazon Simple Storage Service(Amazon S3) 특징

-원하는 양의 데이터를 저장, 검색, 삭제 가능

-내구성과 확장성이 뛰어나며 사용한 스토리지 용량만큼 요금이 청구

-저장할 수 있는 파일 수의 제한 x

-최소 1바이트에서 최대 5TB의 데이터를 저장하고 서비스 제공 가능

-파일에 인증을 붙여서 무단으로 엑세스 하지 못하도록 가능

-정보의 중요도에 따라서 보호 수준을 차등화 가능

-버킷(bucket)과 키(key)로 구성

Amazon Simple Storage Service(Amazon S3) 사용 용어

객체

저장된 데이터(파일) 하나 하나를 객체라고 명명

버킷

연관된 객체들을 그룹핑한 최상위 디렉토리

버킷 단위로 지역 지정 가능

버킷에 포함된 모든 객체에 대해 일괄적으로 인증 및 접속 제한 가능

버킷 내 객체의 고유한 식별자

버킷 내 모든 객체는 고유한 키를 가짐

버전 관리

저장된 객체들의 변화를 저장

RSS(Reduced Redundancy Storage)

객체에 비해 데이터가 손실될 확률이 높은 형태의 저장 방식

대신 가격 저렴

복원이 가능한 데이터를 저장하는데 적합

 

Amazon Simple Storage Service(Amazon S3) 간략한 사용 흐름

AWS S3 페이지에서 버킷 생성

버킷 생성시 원하는 지역 설정

버킷 퍼블릭 액세스 설정

버킷에 객체(파일) 업로드

업로드시 권한 관련 설정 진행

업로드 후 다운로드 가능

ec2에서 s3 접근 위해서는 IAM 역할 설정 필요

 

참고 사이트

https://aws.amazon.com/ko/s3/

https://seoyeonhwng.medium.com/aws-s3%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-b0da502b0504

https://dev.classmethod.jp/articles/for-beginner-s3-explanation/

https://vlee.kr/4765

 

개발 환경

스프링 부트  2.4.0 + ubuntu 20.04+ nginx1.18.0



진행 중인 프로젝트에서 기존 로그인 로직 수정 요청이 들어오며 추가로 다날 SMS 본인인증 처리가 필요해졌다.

서브 도메인을 연결하여 SMS 인증 페이지를 띄워 웹뷰를 통해 클라이언트 분들이 사용할 수 있도록 해야했고 보안을 위해 access 토큰을 서버사이드에서 받아 클라이언트로 보낼 수 있도록 관련 API를 개발했다.

다날 아임포트사에서 코드 예시와 함께 가이드 라인을 제공해주고 있는데 다 node.js다..깃헙 레포를 통해 java 사용자들도 사용할 수 있도록 maven 플러그인을 제공해주고 있으니 참고하면 좋을 것 같다. 필자는 gradle을 사용하고 있기도하고 제공해주는 모듈에서 안쓰는 기능들이 많아 간단하게 코드로 직접 만들어서 개발했다.
https://github.com/iamport/iamport-rest-client-java

 

GitHub - iamport/iamport-rest-client-java: JAVA사용자를 위한 아임포트 REST API 연동 모듈입니다

JAVA사용자를 위한 아임포트 REST API 연동 모듈입니다. Contribute to iamport/iamport-rest-client-java development by creating an account on GitHub.

github.com

 

<서브 도메인으로 본인인증 페이지 띄우기>

다날 SMS 본인 인증 페이지를 띄울 수 있는 html 파일의 코드는 아래와 같다.

<!DOCTYPE html>
<html>
<head>
<!-- jQuery -->
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
<!-- iamport.payment.js -->
<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.4.js"></script>
</head>

<body>
<script type="text/javascript">
IMP.init('가맹점식별코드를입력해주세요.');
IMP.certification({
merchant_uid : 'merchant_' + new Date().getTime() //본인인증과 연관된 가맹점 내부 주문번호가 있다면 넘겨주세요
}, function(rsp) {
if ( rsp.success ) {
    // 인증성공
    console.log(rsp.imp_uid);
    console.log(rsp.merchant_uid);

$.ajax({
    type : 'POST',
    url : '/certifications/confirm',
    dataType : 'json',
    data : {
    imp_uid : rsp.imp_uid
    }
    }).done(function(rsp) {
    // 이후 Business Logic 처리하시면 됩니다.
    });

} else {
    // 인증취소 또는 인증실패
    var msg = '인증에 실패하였습니다.';
    msg += '에러내용 : ' + rsp.error_msg;

    alert(msg);
    }
});

</script>
</body>
</html>

가맹점 식별 코드를 적어 해당 html 파일을 만들었다면 운영 중인 서버의 원하는 위치에 파일을 위치시킨다.


다음으로 사용 중인 도메인에 서브 도메인을 하나 새로 파자!

필자가 참여 중인 프로젝트는 가비아라는 도메인 판매 사이트를 이용하고 있어 해당 사이트에서 서브 도메인을 추가해줬다.

각자의 상황에 맞는 도메인 판매 사이트에서 서브 도메인 관련 설정을 맞췄다면 ec2 nginx의 설정을 변경해 서브 도메인을 연결해줘야한다.

위 내용은 이전 포스팅에서 다룬 적 있어 해당 포스팅 링크로 대체!

https://cofls6581.tistory.com/64

 

[서브 도메인] 서브도메인 적용 및 프로젝트 폴더 분리 (가비아)

개발용 서버와 배포용 서버를 분리하기 위해 서브 도메인을 적용해보자. 개발용 주소는 dev.도메인 주소로 설정하고 배포용 주소는 prod.도메인 주소를 사용하겠다. 이름은 취사 선택의 문제로 원

cofls6581.tistory.com

root에는 위에서 만든 html 파일이 존재하는 파일 경로를 써주고 index에는 html 파일의 이름을 적어주면 된다.

이후 연결한 서브 도메인으로 접속하면 본인 인증 페이지가 잘 뜸을 확인할 수 있다.

만약, 이때 제대로 뜨지 않는다면 포트 번호와 서버의 인바운드 규칙 등을 확인해보자.


<아임포트 access 토큰 전달 API 구축>

컨트롤러단

    
    private final UserService userService;
    
    @ApiOperation(value = "아임포트 access token 발급 API") //스웨거 사용안할 시 생략 가능
    @GetMapping("/iamports/accessToken")
    public BaseResponse<String> getIamportAccessToken() {
        return new BaseResponse<>(userService.getIamportAccessToken());
    }

 

서비스단

    @Transactional
    public String getIamportAccessToken () {
        RestTemplate restTemplate = new RestTemplate();
        String requestUrl = "https://api.iamport.kr/users/getToken";

        Map<String, String> iamportKey = new HashMap();
        iamportKey.put("imp_key", imp_key); //발급받은 REST API key 값을 넣어주세요
        iamportKey.put("imp_secret",imp_secret); //발급받은 REST API secret 값을 넣어주세요.

        ResponseEntity<Object> responseData = restTemplate.postForEntity(requestUrl, iamportKey, Object.class);
        LinkedHashMap responseBody = (LinkedHashMap) responseData.getBody();
        LinkedHashMap responseBodyProps = (LinkedHashMap) responseBody.get("response");
        String accessToken = (String) responseBodyProps.get("access_token");

        return accessToken;
    }

필자는 키 값들을 노출시키지 않기 위해 다른 파일에 적은 후 git ignore에 해당 파일을 추가하여 개발했다.

 

참고 사이트

https://docs.iamport.kr/tech/mobile-authentication

https://blog.naver.com/iamport/221004352427

https://cordingmonster.tistory.com/76

@Scheduled  어노테이션

주기적으로 해야되는 작업을 쉽게 적용할 수 있도록 도와주는 어노테이션

스프링 3.1 이상부터 지원 

 

@Scheduled  사용 준비

@SpringBootApplication 어노테이션이 있는 어플리케이션 자바 파일에 @EnableScheduling 어노테이션 추가

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@Scheduled  사용 

1. cron 

cron 표현식을 지원

초 분 시 일 월 주(년) 순으로 표기 

@Scheduled(cron = "0 0 9 * * *") //매일 9시마다
public void TestScheduler(){
        System.out.println("테스트");
}
0 ~ 59
0 ~ 59
0 ~ 23
1 ~ 31
1 ~ 12 
요일 1 ~ 7 (1 => 일요일, 7=> 토요일) 
년도 1970 ~ 2099

* : 모든 값

? : 특정 값 없음

- : 범위 지정에 사용

, : 여러 값 지정 구분에 사용

/ : 초기값과 증가치 설정에 사용

L : 지정할 수 있는 범위의 마지막 값

W : 월~금요일 또는 가장 가까운 월/금요일

# : 몇 번째 무슨 요일 2#1 => 첫 번째 월요일

 

사용 예시

 "0 0 02 * * ?" : 매일 새벽 두시

 "0 0 12 * * ?" : 매일 정오

 "0 15 10 ? * *" : 모든 요일, 매월, 아무 날이나 10:15:00 

 "0 15 10 * * ? *" : 모든 연도, 아무 요일, 매월, 매일 10:15 

 "0 0/5 14,18 * * ?" : 아무 요일, 매월, 매일, 14시, 18시 매 5분마다 0초 

 "0 0-5 14 * * ?" : 아무 요일, 매월, 매일, 14:00 부터 매 14:05까지 매 분 0초 

 "0 10,30 14 ? 6 WED" : 6월의 매 수요일, 아무 날짜나 14:10:00, 14:30:00 

 "0 15 10 L * ?" : 아무 요일, 매월 마지막 날 10:15:00 

 "0 15 10 ? * 7L" : 매월 마지막 토요일 아무 날이나 10:15:00 

 "0 15 08 ? * 6L 2002-2005" : 2002년부터 2005년까지 매월 마지막 금요일 아무 날이나 0815:00 

 "0 15 08 ? * 6#3" : 매월 3번째 금요일 아무 날이나 08:15:00

 

2. fixedDelay 

해당 메소드가 종료된 시점부터 다음 메소드 실행 시점까지의 주기

시간 단위는 밀리세컨드

@Scheduled(fixedDelay=1000)
public void TestScheduler(){
        System.out.println("테스트");
}

3. fixedRate

해당 메소드가 시작된 시점부터 다음 메소드 실행 시점까지의 주기

시간 단위는 밀리세컨드

@Scheduled(fixedRate=1000)
public void TestScheduler(){
        System.out.println("테스트");
}

 

 

참고 사이트

https://rooted.tistory.com/12

https://toma0912.tistory.com/17

https://jeong-pro.tistory.com/186

https://java119.tistory.com/34
https://javafactory.tistory.com/1386

+ Recent posts