본문으로 건너뛰기

SOLAPI 앱스토어에 내 앱 등록하기 2. 솔라피 API 사용하기

· 약 11분
Eden Cha

이전 글에서 내 앱에 솔라피 OAuth2 인증 연동 방법을 설명하고 사용자의 액세스 토큰을 발행 받았습니다. 이제 발행 받은 액세스 토큰을 이용하여 솔라피 API를 사용하는 방법을 알아봅니다.

이전 글을 확인하려면 아래 링크를 참조합니다.

SOLAPI 앱스토어에 내 앱 등록하기 1. 솔라피 OAuth2 인증

전체 예제 코드는 아래 Github Repo에서 확인할 수 있습니다.

edencha/solapi-desktop-app-demo

API로 잔액과 발신번호 정보 조회하기

토큰을 성공적으로 발행 받고 내 서버에 저장했다면 이제 클라이언트 앱에서는 토큰 발행 시 사용자가 허용한 권한 내에서 솔라피 API를 이용한 정보 조회가 가능합니다.

토큰은 서버에 존재하므로, 토큰 생성 시 이용한 state를 통해 토큰을 탐색합니다. 이를 위해 먼저 로그인에 성공하는 경우 일렉트론 세션 쿠키에 state를 저장합니다.

main.js
      if (pathname !== '/login-success') return
const cookie = {
url: config.apiHost,
name: 'state',
value: String(state)
}
await setCookie(cookie)
win.webContents.send('after-login', cookie)
child.close()

잔액과 발신번호 정보를 출력하기 위해 솔라피 서버에 요청합니다.

main.js
ipcMain.on('get-senderids', async (e, state) => {
try {
const { data } = await axios.get(
config.apiHost + '/senderids?state=' + state
)
win.webContents.send('senderids', data.senderids)
} catch (error) {
console.error(error)
}
})

function getBalance(state) {
return axios.get(config.apiHost + '/balance?state=' + state)
}

ipcMain.on('get-balance', async (e, state) => {
try {
const { data } = await getBalance(state)
win.webContents.send('balance', data.balance)
} catch (error) {
console.error(error)
}
})
renderer.js
ipcRenderer.on('after-login', (e, cookie) => {
const loginForm = document.querySelector('.login-form')
const messageForm = document.querySelector('.textmessage-form')
loginForm.style.display = cookie ? 'none' : 'block'
messageForm.style.display = cookie ? 'block' : 'none'
if (cookie) {
ipcRenderer.send('get-senderids', cookie.value)
ipcRenderer.send('get-balance', cookie.value)
}
})
server.js
app.use('/senderids', async (req, res, next) => {
try {
const { state } = req.query
const accessToken = await getAccessToken(state)
const headers = { Authorization: `Bearer ${accessToken}` }
const requestUrl = 'https://api.solapi.com/senderid/v1/numbers/active'
const { data: senderids } = await axios.get(requestUrl, { headers })
res.send({ senderids })
} catch (error) {
next(error)
}
})

app.use('/balance', async (req, res, next) => {
try {
const { state } = req.query
const accessToken = await getAccessToken(state)
const headers = { Authorization: `Bearer ${accessToken}` }
const requestUrl = 'https://api.solapi.com/cash/v1/balance'
const { data: balance } = await axios.get(requestUrl, { headers })
res.send({ balance })
} catch (error) {
next(error)
}
})

state를 이용해서 서버에서 액세스 토큰을 사용합니다. 올바르게 솔라피 서버에서 데이터를 받아온 경우 렌더러 프로세스에 의해서 화면에 표시되어야 합니다.

서버에서 잔액 정보를 가져온 모습

메시지 발송 기능 추가하기

정보 조회와 마찬가지로 솔라피 서버에 문자 접수 요청해야 합니다.

index.html
      <div>
<p>3. 문자 내용 입력</p>
<textarea
name="message"
id="message"
cols="40"
rows="10"
onkeyup="onChangeTextMessage()"
></textarea>
<p>
<span class="size">0</span>/<span class="max-size">90</span>
Byte
</p>
</div>
<div>
<p>4. 문자 발송</p>
<button type="button" onclick="onClickSendMessage()">전송</button>
</div>
main.js
ipcMain.on('send-message', (e, message, from, to) => {
session.defaultSession.cookies.get(
{ name: 'state' },
async (error, cookies) => {
const cookie = cookies[0]
const state = cookie.value
try {
const result = await axios.post(config.apiHost + '/send', {
from,
to,
message,
state
})
const { data } = await getBalance(state)
win.webContents.send('balance', data.balance)
win.webContents.send('response', result.data)
} catch (error) {
const { response } = error
win.webContents.send('response', response.data)
}
}
)
})
renderer.js
function onClickSendMessage() {
const message = messageEl.value
const option = selectEl.options[selectEl.selectedIndex]
const selSenderId = (option || {}).value
if (!confirm('정말로 발송합니까?')) return
const to = recipentEl.value
ipcRenderer.send('send-message', message, selSenderId, to)
}
server.js
app.use('/send', async (req, res, next) => {
try {
const { to, from, message, state } = req.body
const accessToken = await getAccessToken(state)
config.init({ accessToken })
const response = await Group.sendSimpleMessage(
{
type: 'SMS',
to,
from,
text: message
}
)
res.send(response)
} catch (error) {
next(error)
}
})

전송 버튼을 클릭하면 메인 프로세스에 입력한 내용을 토대로 서버로 문자 발송 요청을 합니다.

서버에서 솔라피 NodeJS용 message SDK를 이용하여 문자를 발송할 수 있습니다. 한 번에 여러 명 또는 여러 개의 문자를 발송해야 하는 경우 sendSimpleMessage를 사용하지 않고, 그룹메시지를 이용합니다.

대량 발송에 관한 자세한 내용은 솔라피 문서에서 확인할 수 있습니다. https://docs.solapi.com/rest-api-reference/message-api-v4/undefined-1

솔라피 홈페이지 문자메시지 전송 내역에서 직접 확인해 보도록 하겠습니다.

문자 접수 직후 발송중인 화면

문자 발송이 완료된 화면

전송 결과 페이지(https://solapi.com/message-log)에서 발송한 메시지의 그룹 목록을 조회할 수 있습니다. sendSimpleMessage는 발송 시 마다 그룹을 새로 생성합니다.

수익내역 페이지 (https://solapi.com/me/apps/settle)

현재는 문자 발송에 성공해도 어떤 앱에서 발송된 문자인지 솔라피에서 알 수 없으므로 수익금도 발생하지 않습니다. 먼저 수익금을 설정합니다.

앱 정보 페이지에서 문자별 앱 수익금을 설정한 모습

수익금을 4원으로 설정했으므로 문자 발송 시, 기본단가 + 4원이 사용자의 잔액(또는 포인트)에서 차감됩니다. 기본 문자 단가 11원에서 4원을 더해 총 15원이 차감됩니다.

server.js
app.use('/send', async (req, res, next) => {
try {
const { to, from, message, state } = req.body
const accessToken = await getAccessToken(state)
config.init({ accessToken })
const response = await Group.sendSimpleMessage(
{
type: 'SMS',
to,
from,
text: message
},
{
appId: 'OavaIZnGRICS'
}
)
res.send(response)
} catch (error) {
next(error)
}
})

수익 발생을 위해 sendSimpleMessage 메서드에 내 앱의 appId를 추가했습니다. 이제 문자 메시지 발송 시 내가 설정한 금액으로 발송하게 되고, 추가 수익금을 설정했다면 수익금이 누적됩니다.

APP ID가 추가된 채로 발송된 문자 메시지

수익금이 포함되어 차감된 금액이 15원으로 표시되는 모습

전송 내역에서 성공적으로 앱 아이디가 포함되었는지, 수익금인 4원이 추가된 금액이 차감되었는지 확인합니다. 앱 아이디를 포함하지 않고 발송했을 때 차감된 금액과 비교해보세요.

정산 예정 금액이 쌓인 모습

다시 수익 내역 페이지를 확인합니다. 정산 예정 금액이 계속 표시되지 않는다면, 포인트로 인해 발송된 문자가 아닌지 확인합니다. 포인트로 인해 발송된 문자는 수익이 발생하지 않습니다. 잔액과 포인트 중 포인트가 우선적으로 차감됩니다.

문자를 발송하는 사용자의 잔액이 1000원, 포인트가 13원이 있다고 가정하면, 포인트가 우선 차감되어 문자 발송 시 포인트 13원, 캐시 2원이 차감됩니다. 따라서 앱 수익금은 4원이 아닌 2원이 됩니다.

다음 장에서 다루지만, 수익금 설정 후 ‘LIVE’ 모드로 변경하는 경우 수익금을 수정할 수 없으니 개발 단계에서 신중하게 결정한 후, LIVE 모드로 변경해야 합니다.

‘LIVE’모드로 변경하면 솔라피 앱스토어 앱 목록에 공개되어 다른 계정이 내 앱을 통해 발송할 수 있습니다. (Live 전환을 하지 않은 경우, 내 계정의 회원만 발송할 수 있습니다.)

로그아웃 기능 추가하기

index.html
      <div>
<button onclick="onClickLogout()">로그아웃</button>
</div>
main.js
ipcMain.on('request-logout', async e => {
child = new BrowserWindow({ parent: win, show: false })
child.setMenu(null)
const logoutPage = config.solapiHost + '/oauth2/v1/logout'
child.loadURL(logoutPage)
await clearCookie()
win.webContents.send('after-login', await isLogin())
child.close()
})
renderer.js
function onClickLogout() {
selectEl.innerHTML = ''
selectEl.options.add(defaultOptionEl)
balanceEl.innerHTML = 0
pointEl.innerHTML = 0
responseEl.innerHTML = ''
recipentEl.value = ''
messageEl.value = ''
sizeEl.innerHTML = 0
ipcRenderer.send('request-logout')
}

로그아웃 버튼 클릭 시 화면을 초기화 합니다.

메인 프로세스에 로그아웃 요청이 들어오면 브라우저와 일렉트론 세션 쿠키를 모두 삭제해야 합니다. 쿠키에 있던 state값이 사라지면 서버에 저장된 토큰을 사용할 수 없게 됩니다. 서버에 저장된 토큰도 삭제합니다.

https://api.solapi.com/oauth2/v1/logout

위에 리소스에 접근해서 브라우저 쿠키에 있는 토큰을 삭제할 수 있습니다.

이렇게 앱을 통해 문자를 발송하고 수익이 잘 누적되는지 확인해 보았습니다. 다음 글에서는 누구나 내 앱을 사용할 수 있도록 배포하는 방법에 대해서 알아보겠습니다.