출근하자마자 아침에 개발팀에 올라왔던 SFDC 25 릴리즈를 확인했는데
전반적으로 기능 개선을 하려고 노력하는 모습이 보이긴 했지만
한참 전부터 당연히 돼야 한다고 했던 부분들이 이제 조금씩 적용되는 것이기 때문에
실제 사용자가 아니라면 감흥보다는 이게 원래 안됐다고? 라는 생각이 들 것 같았다.
작업 목록을 정리해준 다음 회식 논의를 했고
마이그레이션 관련 회의에 참여했는데 30분으로 예정되었지만 1시간을 넘게 하게 되었다.
대부분 마이그레이션은 답이 좀 없는 느낌이 있었는데
저쪽에서도 정리를 해야 하고 이쪽에서도 맵핑을 해야 하고
정의서가 딱 떨어져서 인터페이스를 하는게 아니다보니 뭔가 복잡해보였다.
주간 작업 목록을 정리해준 다음
파일의 용량 제한 때문에 SFDC에 blob으로 받을 수 없고
파일 링크로 받게 되면 이후 수동으로 등록하는 파일과 구분되어 문제가 된다는 이야기가 나왔는데
이전 담당하던 홈페이지 이전쪽도 동일한 논의가 있어서 파일 전송 부분을 해결했었지만
협력사쪽의 편의에 의해 링크로 이전되어 사용하지 않게 된 코드를 다시 사용해봤다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blob 데이터 Salesforce 업로드</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
input[type="text"], button {
margin-bottom: 10px;
padding: 5px;
font-size: 16px;
}
#result {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>Blob 데이터 Salesforce 업로드</h1>
<label for="authToken">Salesforce Authorization Token:</label>
<input type="text" id="authToken" placeholder="Authorization Token 입력" size="50">
<input type="text" id="fileLink" placeholder="파일 다운로드 링크 입력" size="50">
<button id="uploadButton">파일 업로드</button>
<div id="result"></div>
<script>
document.getElementById('uploadButton').addEventListener('click', async () => {
const fileLink = document.getElementById('fileLink').value;
const authToken = document.getElementById('authToken').value;
const resultDiv = document.getElementById('result');
if (!fileLink || !authToken) {
alert("파일 다운로드 링크와 Authorization Token을 입력하세요.");
return;
}
try {
// 파일 다운로드
const response = await fetch(fileLink);
if (!response.ok) {
throw new Error(`HTTP 오류: ${response.status}`);
}
const blob = await response.blob();
const fileName = fileLink.split('/').pop(); // 파일명 추출
const base64Data = await blobToBase64(blob);
console.log('다운로드 성공! Blob:', blob);
// Salesforce API 요청을 프록시 서버로 중계
const salesforceResponse = await fetch('<http://localhost:3000/upload>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
Title: fileName,
PathOnClient: fileName,
FirstPublishLocationId: "0035g000007GALTAA4",//테스트용도로 input에서 입력받아서 처리되게 수정 필요
VersionData: base64Data,
Token: authToken
}),
});
if (!salesforceResponse.ok) {
throw new Error(`업로드 실패: ${salesforceResponse.statusText}`);
}
resultDiv.innerHTML = `<p>파일 업로드 성공!</p>`;
} catch (error) {
console.error('업로드 실패:', error.message);
resultDiv.innerHTML = `<p style="color: red;">업로드 실패: ${error.message}</p>`;
}
});
// Blob을 Base64로 변환하는 함수
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]); // Base64 데이터 반환
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
</script>
</body>
</html>
처음에는 위 코드처럼 하지 않고 단계적으로 진행해서
파일을 blob으로 변환이 되는지 체크하고
그 다음에는 다운로드 링크 파일을 다운받아서 blob으로 변환이 되는지 확인하고
마지막으로 blob으로 변환된 내용을 sfdc 파일생성 전송이 되는지 확인했는데
이 전송 부분을 수동으로 만든 인터페이스가 아니라
https://xxxxxxx.sandbox.my.salesforce.com/services/data/v56.0/sobjects/ContentVersion 이라는
SFDC에서 기본적으로 제공하는 api를 사용해서 테스트했다.
하지만 다운로드 부분에서 cors가 막혔기 때문에
크롬 확장프로그램인 Allow CORS: Access-Control-Allow-origin를 사용해서 해결했지만
SFDC로 발송하는 부분은 CORS 무시가 통하지 않았기 때문에 결국 Node.JS 서버를 만들어야 했다.
//server.js
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors'; // CORS 추가
import fetch from 'node-fetch';
const app = express();
const PORT = 3000;
app.use(cors()); //CORD 에러 해결
// 요청 크기 제한 증가시키기
app.use(bodyParser.json({ limit: '10mb' }));
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }));
app.post('/upload', async (req, res) => {
const { Title, PathOnClient, FirstPublishLocationId, VersionData, Token } = req.body;
try {
const salesforceResponse = await fetch('<https://xxxxxxx.sandbox.my.salesforce.com/services/data/v56.0/sobjects/ContentVersion>', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + Token,
'Content-Type': 'application/json',
},
body: JSON.stringify({
Title,
PathOnClient,
FirstPublishLocationId,
VersionData,
}),
});
if (!salesforceResponse.ok) {
const errorText = await salesforceResponse.text();
res.status(salesforceResponse.status).send(`Salesforce 오류: ${errorText}`);
return;
}
res.send('업로드 성공!');
} catch (error) {
console.error('Salesforce 업로드 실패:', error);
res.status(500).send('업로드 실패');
}
});
app.listen(PORT, () => {
console.log(`서버가 ${PORT}번 포트에서 실행 중입니다.`);
});
CORS는 서버를 통해도 되지 않아서 CORS를 import하고 use로 설정한 다음에서야 제대로 작동했고
그 이후에는 필요한 값들을 넘겨서 다운로드 링크와 토큰을 넘겨서 SFDC에 생성되는 것을 확인했다.
이제 다운로드링크로 마이그레이션을 진행한 레코드들을 쿼리로 모아서
Id와 다운로드 링크 리스트만 있으면 각각 인터페이스 호출을 통해서 발송할 수 있게 할 수 있을 것 같지만
이 방식대로 진행하지 않을 수도 있다고 하셔서 일단 리스트로 파일 배치 대규모 처리는 여유가 되면 해보기로 했다.
(1).백준 3049번 다각형의 대각선은 내부 점들을 이어서 교차점의 숫자를 구해야 하는 문제였다.
nC4를 구하라는 것과 같은 내용이며 4개가 되기 전에는 0이기 때문에
nC4를 구해준 다음 값이 1보다 작으면 0을 출력하고 아니면 계산된 결과를 출력하는 방식으로 해결했다.
const input = Number('6')
const result = input * (input - 1) * (input - 2 ) * (input - 3) / 24
console.log( result >= 1 ? result : 0)