NotificationService를 통해 카카오 알림톡을 쉽게 발송할 수 있습니다.
다른 MSA 서비스(MemberService, AuthService 등)에서 REST API를 호출하여 알림톡을 발송하세요.
# 활성화된 템플릿 목록 조회
curl -X GET "http://localhost/api/alimtalk/templates?activeOnly=true"
응답 예시:
{
"success": true,
"data": [
{
"templateId": "amano_hub_member_registered",
"templateName": "회원가입 완료",
"message": "[아마노파킹]\n회원가입을 축하합니다!\n...\n아이디: #{아이디명}\n가입 일시: #{가입날짜}",
"requiredParams": "아이디명,가입날짜"
}
]
}
curl -X POST http://localhost/api/alimtalk/send \
-H "Content-Type: application/json" \
-H "X-Service-Name: MemberService" \
-d '{
"templateId": "amano_hub_member_registered",
"phoneNumber": "010-1234-5678",
"templateParams": {
"아이디명": "hong@example.com",
"가입날짜": "2025-11-16 14:30"
}
}'
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// X-Service-Name 헤더 자동 추가 (서비스명은 application.yml에서 관리)
restTemplate.getInterceptors().add((request, body, execution) -> {
request.getHeaders().set("X-Service-Name", "MemberService"); // 서비스명 변경 필요
return execution.execute(request, body);
});
return restTemplate;
}
}
package com.akc.b2c.memberservice.client;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
/**
* 알림톡 발송 클라이언트
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AlimtalkClient {
private final RestTemplate restTemplate;
@Value("${notification-service.url:http://localhost}")
private String notificationServiceUrl;
/**
* 회원가입 환영 알림톡 발송
*/
public void sendWelcomeMessage(String phoneNumber, String email) {
String url = notificationServiceUrl + "/api/alimtalk/send";
// 요청 데이터 생성
Map<String, Object> request = new HashMap<>();
request.put("templateId", "amano_hub_member_registered");
request.put("phoneNumber", phoneNumber);
// 템플릿 파라미터
Map<String, String> params = new HashMap<>();
params.put("아이디명", email);
params.put("가입날짜", LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
request.put("templateParams", params);
try {
// NotificationService 호출
ResponseEntity<Map> response = restTemplate.postForEntity(
url,
request,
Map.class
);
log.info("알림톡 발송 성공: phoneNumber={}, response={}",
phoneNumber, response.getBody());
} catch (Exception e) {
// 알림톡 실패는 핵심 비즈니스에 영향 주지 않도록
log.error("알림톡 발송 실패: phoneNumber={}, error={}",
phoneNumber, e.getMessage(), e);
}
}
/**
* 동호수 승인 알림톡 발송
*/
public void sendUnitApprovalMessage(String phoneNumber, String parkingLotName,
String dong, String ho, LocalDateTime approvedAt) {
String url = notificationServiceUrl + "/api/alimtalk/send";
Map<String, Object> request = new HashMap<>();
request.put("templateId", "member_unit_approved");
request.put("phoneNumber", phoneNumber);
Map<String, String> params = new HashMap<>();
params.put("주차장명", parkingLotName);
params.put("동", dong);
params.put("호", ho);
params.put("승인일시", approvedAt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
request.put("templateParams", params);
try {
restTemplate.postForEntity(url, request, Map.class);
log.info("동호수 승인 알림톡 발송 성공: phoneNumber={}", phoneNumber);
} catch (Exception e) {
log.error("동호수 승인 알림톡 발송 실패: phoneNumber={}, error={}",
phoneNumber, e.getMessage());
}
}
}
@Service
@RequiredArgsConstructor
public class MemberService {
private final AlimtalkClient alimtalkClient;
private final MemberRepository memberRepository;
@Transactional
public void signUp(SignUpRequest request) {
// 1. 회원 등록
Member member = Member.builder()
.email(request.getEmail())
.phoneNumber(request.getPhoneNumber())
.build();
memberRepository.save(member);
// 2. 알림톡 발송 (비동기 권장)
try {
alimtalkClient.sendWelcomeMessage(
member.getPhoneNumber(),
member.getEmail()
);
} catch (Exception e) {
// 알림톡 실패해도 회원가입은 성공 처리
log.warn("알림톡 발송 실패했지만 회원가입은 완료: memberId={}", member.getId());
}
}
}
@Component
@RequiredArgsConstructor
public class AlimtalkWebClient {
private final WebClient webClient;
@Value("${notification-service.url:http://localhost}")
private String notificationServiceUrl;
public Mono<Map> sendAlimtalk(String templateId, String phoneNumber,
Map<String, String> templateParams) {
Map<String, Object> request = new HashMap<>();
request.put("templateId", templateId);
request.put("phoneNumber", phoneNumber);
request.put("templateParams", templateParams);
return webClient.post()
.uri(notificationServiceUrl + "/api/alimtalk/send")
.header("X-Service-Name", "MemberService") // 서비스명 변경 필요
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.retrieve()
.bodyToMono(Map.class)
.doOnSuccess(response ->
log.info("알림톡 발송 성공: response={}", response))
.doOnError(error ->
log.error("알림톡 발송 실패: error={}", error.getMessage()));
}
}
@FeignClient(name = "notification-service", url = "${notification-service.url:http://localhost}")
public interface NotificationServiceClient {
@PostMapping("/api/alimtalk/send")
Map<String, Object> sendAlimtalk(
@RequestHeader("X-Service-Name") String serviceName,
@RequestBody AlimtalkSendRequest request
);
}
// 사용 예시
@Service
@RequiredArgsConstructor
public class SomeService {
private final NotificationServiceClient notificationClient;
public void sendAlimtalk() {
AlimtalkSendRequest request = AlimtalkSendRequest.builder()
.templateId("amano_hub_member_registered")
.phoneNumber("010-1234-5678")
.templateParams(Map.of("아이디명", "test@example.com"))
.build();
notificationClient.sendAlimtalk("MemberService", request);
}
}
필수 파라미터:
아이디명: 사용자 이메일 또는 아이디가입날짜: 가입 일시 (예: "2025-11-16 14:30")사용 예시:
Map<String, String> params = new HashMap<>();
params.put("아이디명", "hong@example.com");
params.put("가입날짜", "2025-11-16 14:30");
필수 파라미터:
주차장명: 주차장 이름동: 동 번호호: 호 번호승인일시: 승인 일시사용 예시:
Map<String, String> params = new HashMap<>();
params.put("주차장명", "종암우림카이저");
params.put("동", "101");
params.put("호", "1503");
params.put("승인일시", "2025-11-16 15:00");
필수 파라미터:
주차장명: 주차장 이름동: 동 번호호: 호 번호거부사유: 거부 사유사용 예시:
Map<String, String> params = new HashMap<>();
params.put("주차장명", "종암우림카이저");
params.put("동", "101");
params.put("호", "1503");
params.put("거부사유", "등록 서류 불일치");
# NotificationService URL 설정
notification-service:
url: http://localhost # 로컬 개발 환경
# url: http://notification-service:8080 # Docker 환경
curl -X GET "http://localhost/api/alimtalk/logs?page=0&size=20"
curl -X GET "http://localhost/api/alimtalk/logs/phone/010-1234-5678"
curl -X GET "http://localhost/api/alimtalk/logs?sendStatus=FAILED"
curl -X GET "http://localhost/api/alimtalk/logs?serviceName=MemberService"
알림톡 발송 실패는 핵심 비즈니스 로직에 영향을 주지 않도록 처리하세요.
// ❌ 나쁜 예: 알림톡 실패 시 회원가입 롤백
@Transactional
public void signUp(SignUpRequest request) {
Member member = memberRepository.save(newMember);
alimtalkClient.sendWelcomeMessage(...); // 예외 발생 시 롤백됨
}
// ✅ 좋은 예: 알림톡 실패해도 회원가입은 성공
@Transactional
public void signUp(SignUpRequest request) {
Member member = memberRepository.save(newMember);
try {
alimtalkClient.sendWelcomeMessage(...);
} catch (Exception e) {
log.warn("알림톡 발송 실패 (회원가입은 완료): memberId={}", member.getId());
}
}
대량 발송이나 성능이 중요한 경우 비동기 처리를 권장합니다.
@Service
@RequiredArgsConstructor
public class AlimtalkAsyncClient {
private final AlimtalkClient alimtalkClient;
@Async
public CompletableFuture<Void> sendAsync(String phoneNumber, String email) {
return CompletableFuture.runAsync(() ->
alimtalkClient.sendWelcomeMessage(phoneNumber, email)
);
}
}
실패 시 재시도를 원하면 Spring Retry를 사용하세요.
@Retryable(
value = {HttpClientErrorException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 2000)
)
public void sendAlimtalk(...) {
// 발송 로직
}
010-1234-5678 (권장)01012345678 (지원)새로운 알림톡 템플릿이 필요하면 다음 정보를 포함해서 요청하세요:
password_reset)인증번호,유효시간)예시:
템플릿 ID: password_reset
템플릿 명칭: 비밀번호 재설정
메시지 내용:
[아마노파킹]
비밀번호 재설정 요청
인증번호: #{인증번호}
유효시간: #{유효시간}
필수 파라미터: 인증번호, 유효시간
사용 시나리오: 사용자가 비밀번호 재설정을 요청할 때
원인: 템플릿 ID가 잘못되었거나 비활성화 상태
해결책:
# 활성화된 템플릿 확인
curl -X GET "http://localhost/api/alimtalk/templates?activeOnly=true"
원인: templateParams에 필수 파라미터가 없음
해결책: 템플릿의 requiredParams를 확인하고 모든 파라미터를 전달하세요.
// ❌ 나쁜 예: 필수 파라미터 누락
Map<String, String> params = new HashMap<>();
params.put("아이디명", "hong@example.com");
// 가입날짜 누락!
// ✅ 좋은 예: 모든 필수 파라미터 포함
Map<String, String> params = new HashMap<>();
params.put("아이디명", "hong@example.com");
params.put("가입날짜", "2025-11-16 14:30");
원인: 이전 버전 코드 사용 중 (메시지 업데이트 안 됨)
해결책: NotificationService를 최신 버전으로 업데이트하세요.