ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] spread sheet 연동하기 2
    Backend/개발 2021. 7. 23. 22:49
    반응형

    TIL 30일차

    [이어지는 글]

    2021.07.21 - [TIL] - 29일차 : spreed sheet 연동하기

    [필요한 기능]

    google drive에 저장되어있는 spread sheet 공유 문서를 읽어와 값 조회하기

    [문제]

    이전에 구현한 방법은 저장된 토큰이 없을 경우 redirect link에 접속해 구글로그인을 하고, 새로운 credential token을 얻어와 서버에 저장하는 방식이다.

    그런데 지금은 사용자마다 뭔가 로그인해서 값을 얻어오는 로직을 겉으로 다 숨겨야할 것 같아서, 프로젝트 자체가 sheet와 연결되는 정도의 구현을 하고싶다. (리다이렉트, 구글 로그인 기능 필요 X)

    [해결]

    다음 블로그에서 힌트를 찾아서 해결하였다.
    https://jsonobject.tistory.com/561


    Google auth library oauth2 http

    https://github.com/googleapis/google-auth-library-java

    <dependency>
      <groupId>com.google.auth</groupId>
      <artifactId>google-auth-library-oauth2-http</artifactId>
      <version>0.27.0</version>
    </dependency>
    // 이번에 사용한 방식
            ServiceAccountCredentials serviceCredentials = ServiceAccountCredentials.fromStream(new FileInputStream("src/main/resources/" + SERVICE_ACCOUNT_FILE_PATH));
            serviceCredentials = (ServiceAccountCredentials) serviceCredentials.createScoped(Arrays.asList(SheetsScopes.SPREADSHEETS_READONLY));
    
    // 이전 글 방식
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
    
        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
              new NetHttpTransport(), JSON_FACTORY, clientSecrets, SCOPES)
              .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
              .setAccessType("offline")
              .build();
        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8080).build();
        Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
    

    리다이렉트 방식에서 서비스계정 인증방식으로 변경했다.

    • 첫번째 방식은 api 사용자 인증정보 - 서비스계정을 생성하고, 그에 해당하는 json파일로 인증한다. spreadsheet에서 서비스계정 이메일을 편집자로 등록하거나, 링크공유를 설정해야한다.
    • 두번째 방식은 Oauth 클라이언트 ID 에서 생성한 계정의 credential json파일을 읽어오고, 구글 로그인을 통해서 토큰을 생성하여 인증한다 . (localhost8080/Callback으로 redierct)

    참고로 구글링 했을 때 많이 나오는 api.client.googleapis.auth.oauth2 GoogleCredential은 deprecated됐다고 나와서 못쓰는줄 알았는데, 이 라이브러리 (google.auth.ouath2)의 GoogleCredentials을 사용하면 되는것 같다.

    오류 목록

    FileNoutFoundException

    # 단순 파일이 존재하지 않을 경우
    java.io.FileNotFoundException: src/main/resources/aaa.json (No such file or directory)

    forbidden 403

    # 제한된 사용자만 볼 수 있는 파일에 서비스계정을 추가하지 않았을 경우
    {
      "code" : 403,
      "errors" : [ {
        "domain" : "global",
        "message" : "The caller does not have permission",
        "reason" : "forbidden"
      } ],
      "message" : "The caller does not have permission",
      "status" : "PERMISSION_DENIED"
    }

    링크가 있는 사용자 모두 가능

    이때는 서비스계정이 필요하지 않을 것 같아서 서비스계정 없이 기본 credential을 생성해서 접근하는 방식을 찾고있는데 잘 안나온다. defaultCredential함수 써보거나,, 뭔가 하면 googlecredentials request had insufficient authentication scopes 에러뜸

    static으로 만든 Utils Class에서는 @Value를 읽어오지 못하는 오류 -> @Component로 해결

    // SheetsUtils.class 
    @Component
    public class SheetsUtils {
        @Value("${api.google.appName}")
        private String appName;
    
        @Value("${api.google.serviceAccountFile}")
        private String SERVICE_ACCOUNT_FILE_PATH;
    
        private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    
    
        public Sheets getSheetsFromServiceAccount() throws GeneralSecurityException, IOException {
            ServiceAccountCredentials serviceCredentials = ServiceAccountCredentials.fromStream(new FileInputStream("src/main/resources/" + SERVICE_ACCOUNT_FILE_PATH));
            serviceCredentials = (ServiceAccountCredentials) serviceCredentials.createScoped(Arrays.asList(SheetsScopes.SPREADSHEETS_READONLY));
    
            Sheets service = new Sheets.Builder(
                    GoogleNetHttpTransport.newTrustedTransport(),
                    JSON_FACTORY,
                    new HttpCredentialsAdapter(serviceCredentials)
            ).setApplicationName(appName).build();
    
            return service;
        }
    }
    
    //------------------------------------------------------------------------------------
    // Test
        @Autowired
        private SheetsUtils sheetsUtils;
    
    
        @Test
        public void getsheet_service방식() throws GeneralSecurityException, IOException {
            Sheets sheets = sheetsUtils.getSheetsFromServiceAccount();
            assertThat(sheets.getApplicationName()).isEqualTo(appName);
        }

    sheet 연결은 service단에 넣으면 안될 것 같아서 Utils로 따로 빼고 static으로 만들어두고 사용하면 좋을 것 같아서 다 static으로 해놨는데, 그러면 @Value()를 못읽어온다. 1편에서도 적어놨는데 static변수에는 @value가 injection되지 않기 때문. 그래서 @Component 사용해서 빈으로 등록해서 service 불러오는 방식으로 바꿨다.


    이제 구글시트에 있는 내용 정리해서 팀정보 , 경기일정, 결과 가져오는거 구현해야겠다 !!

    참고 기능

    인텔리제이 command+alt+l -> 자동정렬

    • ctrl+option+o -> 안쓰는 import 정리

    출처

    본문에 바로 추가.

    댓글

Designed by Tistory.