55import com .google .api .client .json .jackson2 .JacksonFactory ;
66
77import com .google .api .services .sheets .v4 .Sheets ;
8- import com .google .api .services .sheets .v4 .model .UpdateValuesResponse ;
8+ import com .google .api .services .sheets .v4 .model .BatchUpdateValuesRequest ;
9+ import com .google .api .services .sheets .v4 .model .BatchUpdateValuesResponse ;
910import com .google .api .services .sheets .v4 .model .ValueRange ;
10- import com .google .auth .Credentials ;
1111import com .google .auth .http .HttpCredentialsAdapter ;
1212import com .google .auth .oauth2 .GoogleCredentials ;
13+ import dmu .dasom .api .domain .applicant .entity .Applicant ;
1314import dmu .dasom .api .domain .common .exception .CustomException ;
1415import dmu .dasom .api .domain .common .exception .ErrorCode ;
1516import lombok .RequiredArgsConstructor ;
1920import org .springframework .core .io .ClassPathResource ;
2021import org .springframework .stereotype .Service ;
2122
22- import java .io .ByteArrayInputStream ;
2323import java .io .IOException ;
24- import java .nio .charset .StandardCharsets ;
2524import java .security .GeneralSecurityException ;
25+ import java .util .ArrayList ;
2626import java .util .Collections ;
2727import java .util .List ;
2828
@@ -33,19 +33,23 @@ public class GoogleApiService {
3333 private static final Logger logger = LoggerFactory .getLogger (GoogleApiService .class );
3434 private static final String APPLICATION_NAME = "Recruit Form" ;
3535 private static final JsonFactory JSON_FACTORY = JacksonFactory .getDefaultInstance ();
36- @ Value ("${google.credentials.json}" )
37- private String credentialsJson ;
36+ @ Value ("${google.credentials.file.path}" )
37+ private String credentialsFilePath ;
38+ @ Value ("${google.spreadsheet.id}" )
39+ private String spreadSheetId ;
3840 private Sheets sheetsService ;
3941
4042 // Google Sheets API 서비스 객체를 생성하는 메소드
4143 private Sheets getSheetsService () throws IOException , GeneralSecurityException {
4244 if (sheetsService == null ){
43- ByteArrayInputStream credentialsStream = new ByteArrayInputStream ( credentialsJson . getBytes ( StandardCharsets . UTF_8 ) );
45+ ClassPathResource resource = new ClassPathResource ( credentialsFilePath );
4446 GoogleCredentials credentials = GoogleCredentials
45- .fromStream (credentialsStream )
47+ .fromStream (resource . getInputStream () )
4648 .createScoped (Collections .singletonList ("https://www.googleapis.com/auth/spreadsheets" ));
4749
48- sheetsService = new Sheets .Builder (GoogleNetHttpTransport .newTrustedTransport (), JSON_FACTORY , new HttpCredentialsAdapter (credentials ))
50+ sheetsService = new Sheets .Builder (GoogleNetHttpTransport .newTrustedTransport (),
51+ JSON_FACTORY ,
52+ new HttpCredentialsAdapter (credentials ))
4953 .setApplicationName (APPLICATION_NAME )
5054 .build ();
5155 }
@@ -56,17 +60,118 @@ public void writeToSheet(String spreadsheetId, String range, List<List<Object>>
5660 try {
5761 Sheets service = getSheetsService ();
5862 ValueRange body = new ValueRange ().setValues (values );
59- UpdateValuesResponse result = service .spreadsheets ().values ()
63+ service .spreadsheets ().values ()
6064 .update (spreadsheetId , range , body )
6165 .setValueInputOption ("USER_ENTERED" )
6266 .execute ();
63- logger .info ("Updated rows: {}" , result .getUpdatedRows ());
64- } catch (IOException e ) {
65- logger .error ("Failed to write data to the spreadsheet" , e );
67+ } catch (IOException | GeneralSecurityException e ) {
68+ logger .error ("구글 시트에 데이터를 쓰는 데 실패했습니다." , e );
6669 throw new CustomException (ErrorCode .WRITE_FAIL );
67- } catch (GeneralSecurityException e ) {
68- logger .error ("Failed to write data to the spreadsheet" , e );
69- throw new CustomException (ErrorCode .INTERNAL_SERVER_ERROR );
70+ }
71+ }
72+
73+ public void appendToSheet (List <Applicant > applicants ) {
74+ processSheetsUpdate (applicants , true );
75+ }
76+
77+ public void updateSheet (List <Applicant > applicants ) {
78+ processSheetsUpdate (applicants , false );
79+ }
80+
81+ private int findRowIndexByStudentNo (String spreadSheetId , String sheetName , String studentNo ){
82+ try {
83+ List <List <Object >> rows = readSheet (spreadSheetId , sheetName + "!A:L" ); // A열부터 L열까지 읽기
84+
85+ for (int i = 0 ; i < rows .size (); i ++){
86+ List <Object > row = rows .get (i );
87+ if (!row .isEmpty () && row .get (2 ).equals (studentNo )){
88+ return i + 1 ;
89+ }
90+ }
91+ } catch (Exception e ) {
92+ logger .error ("구글시트에서 행 찾기 실패" , e );
93+ }
94+ return -1 ;
95+ }
96+
97+ public List <List <Object >> readSheet (String spreadsheetId , String range ) {
98+ try {
99+ Sheets service = getSheetsService ();
100+ ValueRange response = service .spreadsheets ().values ()
101+ .get (spreadsheetId , range )
102+ .execute ();
103+
104+ return response .getValues ();
105+ } catch (IOException | GeneralSecurityException e ) {
106+ logger .error ("시트에서 데이터를 읽어오는데 실패했습니다." , e );
107+ throw new CustomException (ErrorCode .SHEET_READ_FAIL );
108+ }
109+ }
110+
111+ public int getLastRow (String spreadsheetId , String sheetName ) {
112+ try {
113+ List <List <Object >> rows = readSheet (spreadsheetId , sheetName + "!A:L" ); // A~L 열까지 읽기
114+ return rows == null ? 0 : rows .size (); // 데이터가 없으면 0 반환
115+ } catch (Exception e ) {
116+ logger .error ("Failed to retrieve last row from Google Sheet" , e );
117+ throw new CustomException (ErrorCode .SHEET_READ_FAIL );
118+ }
119+ }
120+
121+ public void batchUpdateSheet (String spreadsheetId , List <ValueRange > valueRanges ) {
122+ try {
123+ Sheets service = getSheetsService ();
124+
125+ // BatchUpdate 요청 생성
126+ BatchUpdateValuesRequest batchUpdateRequest = new BatchUpdateValuesRequest ()
127+ .setValueInputOption ("USER_ENTERED" ) // 사용자 입력 형식으로 값 설정
128+ .setData (valueRanges ); // 여러 ValueRange 추가
129+
130+ // BatchUpdate 실행
131+ BatchUpdateValuesResponse response = service .spreadsheets ().values ()
132+ .batchUpdate (spreadsheetId , batchUpdateRequest )
133+ .execute ();
134+
135+ logger .info ("Batch update completed. Total updated rows: {}" , response .getTotalUpdatedRows ());
136+ } catch (IOException | GeneralSecurityException e ) {
137+ logger .error ("Batch update failed" , e );
138+ throw new CustomException (ErrorCode .SHEET_WRITE_FAIL );
139+ }
140+ }
141+
142+
143+ private ValueRange createValueRange (String range , List <List <Object >> values ) {
144+ return new ValueRange ()
145+ .setRange (range )
146+ .setMajorDimension ("ROWS" ) // 행 단위로 데이터 설정
147+ .setValues (values );
148+ }
149+
150+ public void processSheetsUpdate (List <Applicant > applicants , boolean isAppend ) {
151+ try {
152+ List <ValueRange > valueRanges = new ArrayList <>();
153+ int lastRow = isAppend ? getLastRow (spreadSheetId , "Sheet1" ) : -1 ;
154+
155+ for (Applicant applicant : applicants ) {
156+ String range ;
157+ if (isAppend ) {
158+ range = "Sheet1!A" + (lastRow + 1 );
159+ lastRow ++;
160+ } else {
161+ int rowIndex = findRowIndexByStudentNo (spreadSheetId , "Sheet1" , applicant .getStudentNo ());
162+ if (rowIndex == -1 ) {
163+ logger .warn ("구글시트에서 사용자를 찾을 수 없습니다. : {}" , applicant .getStudentNo ());
164+ continue ;
165+ }
166+ range = "Sheet1!A" + rowIndex + ":L" + rowIndex ;
167+ }
168+ valueRanges .add (createValueRange (range , List .of (applicant .toGoogleSheetRow ())));
169+ }
170+
171+ batchUpdateSheet (spreadSheetId , valueRanges );
172+ } catch (Exception e ) {
173+ logger .error ("구글시트 업데이트에 실패했습니다." , e );
174+ throw new CustomException (ErrorCode .SHEET_WRITE_FAIL );
70175 }
71176 }
72177
0 commit comments