Spring Boot에서 프로젝트를 생성하여 Firestore에 있는 데이터 작업을 하려고 합니다.  


Firestore spring boot crud


Firebase에는 다음과 같이 collection을 만들어서 데이터를 입력 하였습니다.


Firestore spring boot crud


이 데이터베이스에 데이터를 조회, 추가, 수정, 삭제하는 기능을 Spring Boot를 이용하여 만들어 보려고 합니다.

 

Spring Boot에서 Spring Starter Project를 선택하여 신규 프로젝트를 생성 합니다.


Firestore spring boot crud


입력 사항은 테스트이니까 대충 적어 넣고 Next 버튼을 클릭 합니다.


Firestore spring boot crud


 

DependencySpring Web만 추가 했습니다.  Finish 버튼을 클릭 합니다.

 

pom.xmlfirebase를 추가 합니다현재 6.13.x 버전까지 나와있는데 한 단계 낮추어서 적어봤습니다.

 

               <dependency>

                   <groupId>com.google.firebase</groupId>

                   <artifactId>firebase-admin</artifactId>

                   <version>6.12.1</version>

                </dependency>

 

Firebase의 필드에 해당하는 Vo를 생성 합니다.

 

package com.copycoding.firebase;

 

public class Member {

        String id;

        String name;

        int age;

        String tel;

       

        public String getId() {

               return id;

        }

        public void setId(String id) {

               this.id = id;

        }

        public String getName() {

               return name;

        }

        public void setName(String name) {

               this.name = name;

        }

        public int getAge() {

               return age;

        }

        public void setAge(int age) {

               this.age = age;

        }

        public String getTel() {

               return tel;

        }

        public void setTel(String tel) {

               this.tel = tel;

        }

}

 

 

Service를 생성하기 위해 먼저 Interface 파일을 생성하고

 

package com.copycoding.firebase;

 

public interface FirebaseService {

 

        public insertMember(Member member) throws Exception;

       

        public Member getMemberDetail(String id) throws Exception;

       

        public String updateMember(Member member) throws Exception;

       

        public string deleteMember(String id) throws Exception;

}

 

 

 

Service Implement도 작성 합니다.

 

package com.copycoding.firebase;

 

import org.springframework.stereotype.Service;

 

import com.google.api.core.ApiFuture;

import com.google.cloud.firestore.DocumentReference;

import com.google.cloud.firestore.DocumentSnapshot;

import com.google.cloud.firestore.Firestore;

import com.google.cloud.firestore.WriteResult;

import com.google.firebase.cloud.FirestoreClient;

 

@Service

public class FirebaseServiceImpl implements FirebaseService {

 

        public static final String COLLECTION_NAME="member";

       

        @Override

        public String insertMember(Member member) throws Exception {

               Firestore firestore = FirestoreClient.getFirestore();

               ApiFuture<WriteResult> apiFuture = firestore.collection(COLLECTION_NAME).document(member.getId()).set(member);

               return apiFuture.get().getUpdateTime().toString();

        }

 

        @Override

        public Member getMemberDetail(String id) throws Exception {

               Firestore firestore = FirestoreClient.getFirestore();

               DocumentReference documentReference = firestore.collection(COLLECTION_NAME).document(id);

               ApiFuture<DocumentSnapshot> apiFuture = documentReference.get();

               DocumentSnapshot documentSnapshot = apiFuture.get();

               Member member = null;

               if(documentSnapshot.exists()) {

                       member = documentSnapshot.toObject(Member.class);

                       return member;

               } else {

                       return null;

               }

        }

 

        @Override

        public String updateMember(Member member) throws Exception {

               Firestore firestore = FirestoreClient.getFirestore();

               ApiFuture<WriteResult> apiFuture = firestore.collection(COLLECTION_NAME).document(member.getId()).set(member);

               return apiFuture.get().getUpdateTime().toString();

        }

 

        @Override

        public String deleteMember(String id) throws Exception {

               Firestore firestore = FirestoreClient.getFirestore();

               ApiFuture<WriteResult> apiFuture = firestore.collection(COLLECTION_NAME).document(id).delete();

              

               return "Document id :" + id + " delete";

        }

}

 

 

 

이제 마지막으로 Controller Rest Api 형식으로 작성 합니다.

 

package com.copycoding.firebase;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

 

@RestController

public class FirebaseController {

 

        @Autowired

        FirebaseService firebaseService;

       

        @GetMapping("/insertMember")

        public String insertMember(@RequestParam Member member) throws Exception {

               return firebaseService.insertMember(member);

        }

       

        @GetMapping("/getMemberDetail")

        public Member getMemberDetail(@RequestParam String id) throws Exception {

               return firebaseService.getMemberDetail(id);

        }

       

        @GetMapping("/updateMember")

        public String updateMember(@RequestParam Member member) throws Exception {

               return firebaseService.updateMember(member);

        }

       

        @GetMapping("/deleteMember")

        public String deleteMember(@RequestParam String id) throws Exception {

               return firebaseService.deleteMember(id);

        }

}

 

여기 까지는 일반 프로젝트와 동일한 방식으로 작성을 하면 됩니다.

 

package com.copycoding.firebase;

 

import java.io.FileInputStream;

import javax.annotation.PostConstruct;

import org.springframework.stereotype.Service;

import com.google.auth.oauth2.GoogleCredentials;

import com.google.firebase.FirebaseApp;

import com.google.firebase.FirebaseOptions;

 

@Service

public class FirebaseInitialize {

 

        @PostConstruct

    public void initialize() {

        try {

        FileInputStream serviceAccount =

                  new FileInputStream("./copycodingServiceAccount.json");

 

                FirebaseOptions options = new FirebaseOptions.Builder()

                  .setCredentials(GoogleCredentials.fromStream(serviceAccount))

                  .setDatabaseUrl("https://copycoding-bca04.firebaseio.com")

                  .build();

 

                FirebaseApp.initializeApp(options);

            } catch (Exception e) {

                e.printStackTrace();

            }

       

        }

}

 

추가로 Firebase에 접속하기 위한 설정 파일을 하나 생성해주면 됩니다.

이렇게 해서 프로그램이 완성 되었습니다.


이제 테스트를 해 봅니다.

귀찮으니 조회만 해 봅니다.


Firestore spring boot crud


조회가 잘 되는 군요.

성의가 없는 것 같아 삭제도 해 봅니다.


Firestore spring boot crud


삭제가 되었다고 하는데 Firebase 데이터베이스에 들어가 확인해 봅니다.


Firestore spring boot crud


document 2번이 삭제 되어 있습니다이런 식으로 작업을 하면 됩니다.


- copy coding -


java로 프로그램을 할 때 파라미터로 가장 많이 사용하게 되는 ObjectVO 객체이고 VO로 값을 받거나 전달해야 하는 경우 VO의 값을 확인 하려면 디버깅모드에서 Variables에 있는 값을 확인 하거나 Vo.getXxx()로 하나씩 출력을 해봐야 하는 불편함이 있는데 ObjectMapper를 이용하면 VoJSON 형태로 변환하여 사용할 수 있습니다.



 

먼제 pom.xml에 라이브러리를 등록 합니다.

 

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->

        <dependency>

            <groupId>com.fasterxml.jackson.core</groupId>

            <artifactId>jackson-databind</artifactId>

            <version>2.11.0</version>

        </dependency>

 

main 으로 사용할 VO 파일을 생성 합니다.

 

public class SampleVo {

 

        private String username; /* 사용자ID*/

        private String name; /* 이름*/

        private Map<String, Object> mobile; /* 휴대전화*/

        private Map<String, Object> tel; /* 전화번호*/

       

        //getXxx, setXxx 생략

       

}

 

VO를 또 생성 합니다이건 Map<String, Object>로 설정한 mobile을 위한 Vo 입니다.


public class MovileVo {

 

        private String mobile1; /* 휴대전화1*/

        private String mobile2; /* 휴대전화2*/

        private String mobile3; /* 휴대전화3*/

       

        //getXxx, setXxx 생략

       

}

 

 

tel을 위한 Vo도 하나 더 추가 합니다.


public class TelVo {

 

        private String tel1; /* 전화번호1*/

        private String tel2; /* 전화번호2*/

        private String tel3; /* 전화번호3*/

       

        //getXxx, setXxx 생략

}

 

 

이제 Controller를 하나 만들고 코딩을 합니다.

MovileVo TelVo에 값을 설정하고 SampleVoMobileVoTelVo를 이용하여 값을 설정 합니다.



import com.fasterxml.jackson.databind.ObjectMapper;


        @RequestMapping(value = "/sample/test")

        public void sampleMethod(Model model) throws Exception {

              

               SampleVo sampleVo = new SampleVo();

               MovileVo movileVo = new MovileVo();

               TelVo telVo = new TelVo();

              

               movileVo.setMobile1("010");

               movileVo.setMobile2("1111");

               movileVo.setMobile3("2222");

              

               telVo.setTel1("02");

               telVo.setTel2("111");

               telVo.setTel3("2222");

              

               Map<String, Object> movileMap = new HashMap<String, Object>();

               Map<String, Object> telMap = new HashMap<String, Object>();

              

               movileMap.put("movile1", movileVo.getMobile1());

               movileMap.put("movile2", movileVo.getMobile2());

               movileMap.put("movile3", movileVo.getMobile3());

              

               telMap.put("tel1", telVo.getTel1());

               telMap.put("tel2", telVo.getTel2());

               telMap.put("tel3", telVo.getTel3());

              

               sampleVo.setUsername("copycoding");

               sampleVo.setName("카피코딩");

               sampleVo.setMobile(movileMap);

               sampleVo.setTel(telMap);

              

               ObjectMapper mapper = new ObjectMapper();

               String samString = mapper.writeValueAsString(sampleVo);

              

               System.out.println("=== sampleVo ===" + samString);

        }

 

ObjectMapper mapper = new ObjectMapper();

String samString = mapper.writeValueAsString(sampleVo);

ObjectMapper를 설정 하고 writeValueAsString()을 이용하면 JSON 형태의 String 값을 얻을 수 있습니다.

 

localhost:8080/sample/test를 호출하여 실행을 하여 log를 확인 해 봅니다.

 

=== sampleVo ==={"username":"copycoding","name":"카피코딩","tel":{"tel1":"02","tel2":"111","tel3":"2222"},"mobile":{"movile1":"010","movile3":"2222","movile2":"1111"}}

 

String형이라 다시 정리 하면

=== sampleVo ===

{

"username":"copycoding",

  "name":"카피코딩",

  "tel":{

        "tel1":"02",

        "tel2":"111",

        "tel3":"2222"

      },

  "mobile":{

        "movile1":"010",

        "movile3":"2222",

        "movile2":"1111"

      }

}

 

이런 결과를 볼 수 있습니다.


- copy coding -


예전에 Quartz를 이용해서 작업할 때 보다는 @Scheduled annotation을 사용하면 배치 작업을 무척 쉽게 만들 수 있습니다.


spring boot Scheduler @Scheduled batch


얼마나 간단하고 쉬운지 한번 만들어 보겠습니다.


spring boot Scheduler @Scheduled batch


먼저 프로젝트를 하나 생성 합니다.


spring boot Scheduler @Scheduled batch


프로젝트 이름을 대충 적어주고


spring boot Scheduler @Scheduled batch


 

화면을 만들건 아니지만 일단 Web을 추가해서 생성 합니다.

 

이제 설정을 해줍니다.


Application 메인 파일을 열고

package com.copycoding.batch;

 

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.scheduling.annotation.EnableScheduling;

 

@SpringBootApplication

@EnableScheduling

public class BatchApplication {

 

        public static void main(String[] args) {

               SpringApplication.run(BatchApplication.class, args);

        }

}

 

@EnableScheduling 어노테이션을 추가해 줍니다.

 

그리고 실제 스케쥴 작업할 class 파일을 만듭니다.

 

package com.copycoding.batch;

 

import java.time.LocalDateTime;

 

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

 

@Component

public class ScheduleTask {

 

@Scheduled(fixedDelay = 2000)

public void task1() {

        System.out.println("The current date (1) : " + LocalDateTime.now());

}

       

@Scheduled(fixedDelayString = "${spring.task.fixedDelay}")

public void task2() {

        System.out.println("The current date (2) : " + LocalDateTime.now());

}}

 

중요한건 class 파일에 @Component를 설정해야 합니다이렇게 두개의 어노테이션을 적어주기만 하면 설정은 끝입니다.

이제 메소드를 만들고 @Scheduled를 이용해서 메소드의 작동 시간을 입력하고 코딩을 해줍니다위에 있는 프로그램을 실행하면


spring boot Scheduler @Scheduled batch


 

이런식으로 결과를 얻을 수 있습니다.  annotation만으로 쉽게 배치 작업이 완료 됩니다.

 

@Scheduled() 어노테이션에 들어가는 설정들을 정리해 보았습니다.

 

fixedDelay

@Scheduled(fixedDelay = 1000)

이전 작업이 종료된 후 설정시간(밀리터리세컨드) 이후에 다시 시작

fixedDelayString

@Scheduled(fixedDelay = “1000”)

fixedDelay와 동일 하고 지연시간(the delay in milliseconds)을 문자로 입력

fixedRate

@Scheduled(fixedRate = 1000)

설정된 시간마다 시작을 한다. 즉 이전 작업이 종료되지 않아도 시작.

fixedRateString

@Scheduled(fixedRateString = “1000”)

fixedRate와 동일 하고 지연시간(the delay in milliseconds)을 문자로 입력

initialDelay

@Scheduled(fixedRate = 5000, initialDelay = 3000)

프로그램이 시작하자마자 작업하는게 아닌 시작을 설정된 시간만큼 지연하여 작동을 시작 한다.(예제는 3초 후 부터 5초 간격으로 작업)

initialDelayString

@Scheduled(fixedRate = 5000, initialDelay = “3000”)

initialDelay와 동일 하고 지연시간(the delay in milliseconds)을 문자로 입력

cron

@Scheduled(cron = "* * * * * *")

첫번째 부터 위치별 설정 값은

(0-59)

(0-59)

시간(0-23)

(1-31)

(1-12)

요일(0-7)

zone

@Scheduled(cron = "0 0 14 * * *" , zone = "Asia/Seoul")

미설정시 local 시간대를 사용한다.

oracle에서 제공하는 문서를 참조하여 입력 한다.

https://docs.oracle.com/cd/B13866_04/webconf.904/b10877/timezone.htm

 

- copy coding -


OAuth2 인증 테스트를 진행할 때 grant typepassword로 설정하여 접속 하는 경우 필요한 파라미터는 grant_type = password, client_id, client_secret, username, password 입니다. 테스트를 진행하다 보면 Unsupported grant type: password 라는 리턴 값을 받는 경우가 있습니다.

{

"error":"unsupported_grant_type",

"error_description":"Unsupported grant type: password"

}

 

실제 리턴되는 형태는

oauth2 grant type password

 

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 :

[{"error":"unsupported_grant_type","error_description":"Unsupported grant type: password"}]

 

일단 오류 결과를 얻었다는 건 OAuth2 Server 작업은 잘 되었다고 볼 수 있습니다그럼 이제 오류만 해결하면 됩니다사실 이건 오류는 아니고 설정이 빠져서 발생하는 부분입니다.


AuthorizationServerConfigurerAdapter를 상속받아 서버 인증 설정을 하게 되는데 상속하는 Method에서 configure(AuthorizationServerEndpointsConfigurer endpoints) 작업을 하지 않은 경우일 가능성이 있습니다해결책은 여기에 AuthenticationManager를 추가해 주면 됩니다.


WebSecurityConfigurerAdapter에 다음과 같이 Bean을 설정 하고

@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

 

    @Override

    @Bean

    public AuthenticationManager authenticationManagerBean() throws Exception {

        return super.authenticationManagerBean();

    }

}

 

 

AuthorizationServerConfig endpoints 추가를 해줍니다.

@Configuration

@EnableAuthorizationServer

public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

 

   @Override

   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

 

          endpoints.authenticationManager(authenticationManager);

   }

 

 }

 

 

그리고 다시 테스트를 하면 원하는 결과를 얻을 수 있습니다.


oauth2 grant type password

oauth2 grant type password


- copy coding -


1234567

+ Recent posts