HD

FCM(firebase) 연동 본문

JAVA

FCM(firebase) 연동

hunecenter 2022. 11. 7. 14:56
반응형

-하이브리드 앱 프로젝트중 클라이언트쪽에서 PUSH알림 기능을 요구해 FCM연동을 처음접하게되었다.

 

-PUSH알림은 사용자가 Q&A를 작성하고 관리자가 답변또는 그해당글을 볼때 발송된다.

 

-이번 프로젝트 하이브리드 앱에 FMC연동 프로세스는 이렇다.

[프로세스]

순서 프로세스
1 사용자가 앱을 실행
2 앱실행시 백단에 만들어 놓은 api url호출 UUID, firebaseToken을 post로 값 전달
3 UUID, firebaseToken를 DB에 저장
4 UUID DB값을 조회해 로그인
5 로그인 사용자가 Q&A글을 작성
6 관리자가 답글을 달거나 해당 글을 보면 사용자한테
알림(PUSH) firebaseToken값을 DB로 조회 FCM API호출
7 핸드폰에서 PUSH 메시지를 클릭했을때 해당앱이 켜짐

- 해당 소스 내용을 참고해서 커스텀 및 Controller를 작성하면 될거같다!

[model]

public class FCMMessage
{
  private boolean validate_only;
  private Message message;

  public static FCMMessageBuilder builder()
  {
    return new FCMMessageBuilder(); } 
  public FCMMessage(boolean validate_only, Message message) { this.validate_only = validate_only; this.message = message; } 
  public boolean isValidate_only() { return this.validate_only; } 
  public Message getMessage() { return this.message; }


  public static class FCMMessageBuilder
  {
    private boolean validate_only;
    private FCMMessage.Message message;

    public FCMMessageBuilder validate_only(boolean validate_only)
    {
      this.validate_only = validate_only; return this; } 
    public FCMMessageBuilder message(FCMMessage.Message message) { this.message = message; return this; } 
    public FCMMessage build() { return new FCMMessage(this.validate_only, this.message); } 
    public String toString() { return "FCMMessage.FCMMessageBuilder(validate_only=" + this.validate_only + ", message=" + this.message + ")"; } 
  }
  public static class Message {
    private FCMMessage.Notification notification;
    private String token; // 특정 device에 알림을 보내기위해 사용

    public static MessageBuilder builder() { return new MessageBuilder(); } 
    public Message(FCMMessage.Notification notification, String token) { this.notification = notification; this.token = token; } 
    public FCMMessage.Notification getNotification() { return this.notification; } 
    public String getToken() { return this.token; }


    public static class MessageBuilder
    {
      private FCMMessage.Notification notification;
      private String token;

      public MessageBuilder notification(FCMMessage.Notification notification)
      {
        this.notification = notification; return this; } 
      public MessageBuilder token(String token) { this.token = token; return this; } 
      public FCMMessage.Message build() { return new FCMMessage.Message(this.notification, this.token); } 
      public String toString() { return "FCMMessage.Message.MessageBuilder(notification=" + this.notification + ", token=" + this.token + ")"; }  } 
  }
  
  /**
   * // 모든 mobile os를 아우를수 있는 Notification
   * @author JHY
   *
   */
  public static class Notification {  
	private String title;
    private String body;
    private String image;

    public static NotificationBuilder builder() { return new NotificationBuilder(); } 
    public Notification(String title, String body, String image) { this.title = title; this.body = body; this.image = image; } 
    public String getTitle() { return this.title; } 
    public String getBody() { return this.body; } 
    public String getImage() { return this.image; }


    public static class NotificationBuilder
    {
      private String title;
      private String body;
      private String image;

      public NotificationBuilder title(String title)
      {
        this.title = title; return this; } 
      public NotificationBuilder body(String body) { this.body = body; return this; } 
      public NotificationBuilder image(String image) { this.image = image; return this; } 
      public FCMMessage.Notification build() { return new FCMMessage.Notification(this.title, this.body, this.image); } 
      public String toString() { return "FCMMessage.Notification.NotificationBuilder(title=" + this.title + ", body=" + this.body + ", image=" + this.image + ")"; }

    }
  }
}

[Service]

public interface FirebaseService {

	/** 결과_성공 */
	public static final String RESULT_SUCCESS = "SUCCESS";
	
	/** 사용자 code 없음 */
	public static final String NO_USER_CODE = "NO_USER_CODE";
	
	/** firebase 토큰 없음 */
	public static final String NO_FIREBASE_TOKEN= "NO_FIREBASE_TOKEN";
	
	/** 결과_실패  */
	public static final String RESULT_FAIL = "FAIL";
	
	//public final String MESSAGING_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
	public final String MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging";
	public final String[] SCOPES = { MESSAGING_SCOPE };
    
    public final String PROJECT_ID = "push-apps-58e3d";
	
    //https://firebase.google.com/docs/cloud-messaging/migrate-v1 참고
    public final String API_URL = "https://fcm.googleapis.com/v1/projects/"+PROJECT_ID+"/messages:send";
	
	//토큰 취득
	public abstract String getAccessToken() throws IOException;
	
	//메시지 발송
	public abstract void sendMessageTo(String userCode, String title, String body, String link) throws IOException;
	
	// 파라미터를 FCM이 요구하는 body 형태로 만들어준다.
	public abstract String makeMessage(String targetToken, String title, String body, String link) throws JsonProcessingException;
	
	//앱 실행시 userCode, firebaseToken값 체크
	public abstract JsonObject firebaseToeknChk(MobilePwd mobilePwd);
	
}

[ServiceImpl]

@Service("FirebaseService")
public class FirebaseServiceImpl implements  FirebaseService{
	
	private static final Logger LOG = LoggerFactory.getLogger( FirebaseServiceImpl.class );
	
	/**
	 * 토큰 취득
	 * <br>
	 * doc : https://firebase.google.com/docs/cloud-messaging/auth-server
	 */
	@Override
	public String getAccessToken() throws IOException {
    	//doc URL 참고
		 String firebaseConfigPath = "../firebase/test.json";
		
		 /*InputStream resource = new FileInputStream(firebaseConfigPath);
		 try ( BufferedReader reader = new BufferedReader(
		   new InputStreamReader(resource)) ) {
		     String readerStr = reader.lines()
		       .collect(Collectors.joining("\n"));
		     System.out.println("파일 읽는지 확인 : " + readerStr);
		 }*/
		 
	     GoogleCredentials googleCredentials = GoogleCredentials
	             .fromStream(new FileInputStream(firebaseConfigPath))
	             .createScoped(Arrays.asList(SCOPES));
	     googleCredentials.refresh();
	     return googleCredentials.getAccessToken().getTokenValue();
	}

	/**
	 * PUSH 메시지 발송
	 * <br>
	 * doc : https://firebase.google.com/docs/cloud-messaging/auth-server
	 */
	@Override
	public void sendMessageTo(String userCode, String title, String body, String link) throws IOException {
		if(!"".equals(ReplaceNull(userCode))) {
			UserInfo userInfo = userDao.getUserInfo(userCode);//회원조회
			if(userInfo!=null) {
				if(!"".equals(ReplaceNull(userInfo.getUserPin()))) {
					String message = makeMessage(userInfo.getUserPin(), title, body, link);

					OkHttpClient client = new OkHttpClient();
			        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),message);
			        Request request = new Request.Builder()
			                .url(API_URL)
			                .post(requestBody)
			                .addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken())
			                .addHeader(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8")
			                .build();

			        Response response = client.newCall(request).execute();
			        /**
			         * success message
			         * <br>
			         * {
			  		 *	 "name": "projects/${projectid}/messages/0:1666597000351490%e54c69bfe54c69bf"
					 * }

			         */
			        LOG.info("result message : "+response.body().string());
				}
				
			}
			
		}
		
	}

	/**
	 * 메시지 생성
	 * <br>
	 * doc : https://firebase.google.com/docs/cloud-messaging/send-message#java_3
	 * <br>
	 * json 형태
	 * <br>
	 * {
	 *    "message":{
	 *       "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
	 *       "notification":{
	 *         "body":"This is an FCM notification message!",
	 *         "title":"FCM Message"
	 *       }
	 *    }
	 * }
	 */
	@Override
	public String makeMessage(String targetToken, String title, String body, String link) throws JsonProcessingException {
		// object를 json 형태 String 으로 반환
		ObjectMapper objectMapper = new ObjectMapper();
		
		FCMMessage fcmMessage = FCMMessage.builder()
                .message(FCMMessage.Message.builder()
                        .token(targetToken)
                        .notification(FCMMessage.Notification.builder()
                                .title(title)
                                .body(body)
                                .image(null)
                                .build()
                        )
                        .build()
                )
                .validate_only(false)
                .build();
		
		LOG.info("fcmMessage json : "+objectMapper.writeValueAsString(fcmMessage));
		
        return objectMapper.writeValueAsString(fcmMessage);
	}

	/**
	 * 
	 */
	@Override
	public JsonObject firebaseToeknChk(String uuid, String firebaseToken) {
		//회원 UUID, FIREBASE_TOKEN등록
	}
}

 

[참고]

https://firebase.google.com/docs/cloud-messaging

 

Firebase 클라우드 메시징

Firebase 클라우드 메시징(FCM)은 비용 없이 안정적으로 메시지를 보낼 수 있는 플랫폼 간 메시징 솔루션입니다.

firebase.google.com

https://sol-devlog.tistory.com/11

 

[SpringBoot] FCM을 통해 Push알림 보내보기

( + 진행 당시, 기능 구현에 집중하다보니 전체적인 설계나 코드가 클린하지 못할 수 있으니 이 부분은 리팩토링 하면서 적용하시면 좋을 것 같아요! :) ) 현재 참여하고 있는 IT연합 동아리 YAPP에

sol-devlog.tistory.com

 

반응형

'JAVA' 카테고리의 다른 글

로그인 세션 시간 표시  (0) 2022.12.13
Java @Scheduled Cron 표현식  (2) 2022.10.04
JAVA 접근장치, 운영체제, 브라우저 확인  (0) 2022.02.12
중복로그인 처리(feat.전자정부)  (0) 2022.01.25
[구글 OTP] java  (4) 2021.09.14
Comments