fix: 회원가입 , oauth 반응형 수정

This commit is contained in:
hong
2026-07-14 14:54:53 +09:00
parent 23bd0c1207
commit bb79b55f08
10 changed files with 652 additions and 463 deletions
+103 -35
View File
@@ -2976,6 +2976,32 @@ body.design-survey-active .global-header {
}
}
.btn-action-primary {
display: inline-flex;
align-items: center;
justify-content: center;
background: #2a69de;
color: #fff;
font-size: 20px;
font-weight: 700;
padding: 16px 48px;
border-radius: 10px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
cursor: pointer;
border: none;
}
.btn-action-primary:hover {
background: rgb(31.8731707317, 92.7219512195, 205.7268292683);
transform: translateY(-2px);
color: #fff;
}
@media (max-width: 768px) {
.btn-action-primary {
font-size: 15px;
}
}
.status-badge {
display: inline-flex;
align-items: center;
@@ -22807,9 +22833,7 @@ body.commission-print-page .btn-primary:hover {
}
}
.service-main {
max-width: 1240px;
margin: 0 auto;
padding: 60px 40px;
padding: 60px 0;
display: flex;
gap: 60px;
align-items: flex-start;
@@ -22817,6 +22841,7 @@ body.commission-print-page .btn-primary:hover {
.service-content {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 40px;
@@ -22903,6 +22928,10 @@ body.commission-print-page .btn-primary:hover {
justify-content: center;
padding: 20px;
}
.service-hero,
.oauth2-2legged__hero {
width: 100vw;
}
}
.service-hero {
background: linear-gradient(to right, #eff9fe, #fcfeff);
@@ -22928,7 +22957,6 @@ body.commission-print-page .btn-primary:hover {
width: 230px;
height: 167px;
position: relative;
background: radial-gradient(circle at 50% 120%, rgba(0, 73, 180, 0.1), transparent 70%);
display: flex;
align-items: center;
justify-content: center;
@@ -22967,6 +22995,11 @@ body.commission-print-page .btn-primary:hover {
color: #000;
margin: 0;
}
@media (max-width: 768px) {
.service-hero__title {
font-size: 29px;
}
}
.service-hero__desc {
font-size: 16px;
font-weight: 500;
@@ -22975,6 +23008,18 @@ body.commission-print-page .btn-primary:hover {
margin: 0;
}
.service-keyImg {
width: 100%;
height: 100%;
object-fit: contain;
}
@media (max-width: 768px) {
.service-keyImg {
width: 90%;
height: 90%;
}
}
.signup-guide-v2 {
background-color: #fff;
padding-bottom: 100px;
@@ -23123,47 +23168,57 @@ body.commission-print-page .btn-primary:hover {
justify-content: center;
margin-top: 20px;
}
.signup-guide-v2 .signup-action__btn {
display: inline-block;
background: #2a69de;
color: #fff;
font-size: 20px;
font-weight: 700;
padding: 16px 48px;
border-radius: 10px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
}
.signup-guide-v2 .signup-action__btn:hover {
background: rgb(31.8731707317, 92.7219512195, 205.7268292683);
transform: translateY(-2px);
}
@media (max-width: 1024px) {
.signup-guide-v2 .signup-hero__inner {
.signup-guide-v2 .service-hero {
padding: 40px 0;
}
.signup-guide-v2 .service-hero__inner {
flex-direction: column;
gap: 30px;
text-align: center;
}
.signup-guide-v2 .signup-hero__content {
.signup-guide-v2 .service-hero__content {
align-items: center;
}
.signup-guide-v2 .signup-info-box {
flex-direction: column;
padding: 20px;
gap: 15px;
text-align: center;
}
.signup-guide-v2 .signup-info-box ul li {
text-align: left;
}
}
@media (max-width: 768px) {
.signup-guide-v2 .signup-step {
gap: 20px;
gap: 15px;
}
.signup-guide-v2 .signup-step__icon-box {
width: 70px;
height: 70px;
width: 60px;
height: 60px;
}
.signup-guide-v2 .signup-step__icon-box svg {
width: 32px;
height: 32px;
width: 24px;
height: 24px;
}
.signup-guide-v2 .signup-step__card {
height: 70px;
padding: 0 20px;
height: auto;
min-height: 60px;
padding: 10px 15px;
}
.signup-guide-v2 .signup-step__card-title {
font-size: 15px;
}
.signup-guide-v2 .signup-timeline {
padding-left: 50px;
}
.signup-guide-v2 .signup-timeline::before {
left: 17px;
}
.signup-guide-v2 .signup-step::before {
left: -41px;
}
}
.service_guide_header {
@@ -23343,13 +23398,11 @@ body.commission-print-page .btn-primary:hover {
}
.oauth2-2legged {
max-width: 1240px;
margin: 0 auto;
background-color: #FFFFFF;
color: #1A1A2E;
display: flex;
flex-direction: column;
box-sizing: border-box;
color: #1A1A2E;
}
.oauth2-2legged *, .oauth2-2legged *::before, .oauth2-2legged *::after {
box-sizing: inherit;
}
.oauth2-2legged__prereq, .oauth2-2legged__sequence, .oauth2-2legged__step, .oauth2-2legged__errors, .oauth2-2legged__error-example {
margin-bottom: 50px;
@@ -23485,6 +23538,8 @@ body.commission-print-page .btn-primary:hover {
background: #FFFFFF;
border: 1px solid #E2E8F0;
border-radius: 14px;
word-break: keep-all;
overflow-wrap: break-word;
}
.oauth2-2legged__prereq-num {
display: inline-flex;
@@ -23519,10 +23574,13 @@ body.commission-print-page .btn-primary:hover {
background: #FFFFFF;
border: 1px solid #E2E8F0;
border-radius: 14px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.oauth2-2legged__sequence-diagram svg {
display: block;
width: 100%;
min-width: 580px;
height: auto;
}
.oauth2-2legged__step-grid {
@@ -23555,6 +23613,7 @@ body.commission-print-page .btn-primary:hover {
font-family: "Fira Code", monospace;
font-size: 16px;
color: #1A1A2E;
word-break: break-all;
}
.oauth2-2legged__endpoint-content-type {
display: inline-block;
@@ -23571,6 +23630,9 @@ body.commission-print-page .btn-primary:hover {
background: #FFFFFF;
border: 1px solid #E2E8F0;
border-radius: 12px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
min-width: 0;
}
.oauth2-2legged__panel-title {
margin: 0 0 14px;
@@ -23586,6 +23648,7 @@ body.commission-print-page .btn-primary:hover {
}
.oauth2-2legged__table {
width: 100%;
min-width: 600px;
border-collapse: collapse;
font-size: 13px;
}
@@ -23661,6 +23724,7 @@ body.commission-print-page .btn-primary:hover {
border-radius: 12px;
background: #1A1A2E;
overflow: hidden;
min-width: 0;
}
.oauth2-2legged__code-panel::before {
content: "";
@@ -23696,6 +23760,7 @@ body.commission-print-page .btn-primary:hover {
line-height: 1.7;
white-space: pre;
overflow-x: auto;
display: block;
}
.oauth2-2legged .o2leg-c {
color: #00D4FF;
@@ -23717,6 +23782,10 @@ body.commission-print-page .btn-primary:hover {
border-radius: 8px;
background: #F8FAFC;
margin-bottom: 6px;
display: flex;
flex-wrap: wrap;
gap: 8px 12px;
align-items: center;
}
.oauth2-2legged__header-key {
display: block;
@@ -23884,7 +23953,6 @@ body.commission-print-page .btn-primary:hover {
}
@media (max-width: 640px) {
.oauth2-2legged {
padding: 16px;
gap: 16px;
}
.oauth2-2legged__hero {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 537 KiB

After

Width:  |  Height:  |  Size: 537 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@@ -573,3 +573,33 @@
height: 36px;
}
}
// -----------------------------------------------------------------------------
// Action Call Button (e.g. Signup, CTA)
// -----------------------------------------------------------------------------
.btn-action-primary {
display: inline-flex;
align-items: center;
justify-content: center;
background: #2a69de;
color: #fff;
font-size: 20px;
font-weight: 700;
padding: 16px 48px;
border-radius: 10px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
cursor: pointer;
border: none;
&:hover {
background: darken(#2a69de, 10%);
transform: translateY(-2px);
color: #fff;
}
@include respond-to('sm') {
font-size: 15px;
}
}
@@ -126,9 +126,7 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
// =============================================================================
.service-main {
max-width: 1240px;
margin: 0 auto;
padding: 60px 40px;
padding: 60px 0;
display: flex;
gap: 60px;
align-items: flex-start;
@@ -136,6 +134,7 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
.service-content {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 40px;
@@ -236,6 +235,12 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
justify-content: center;
padding: 20px;
}
// 사이드바가 세로로 배치되면 가로 여백이 없으므로 100vw로 보정
.service-hero,
.oauth2-2legged__hero {
width: 100vw;
}
}
// =============================================================================
@@ -268,8 +273,6 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
width: 230px;
height: 167px;
position: relative;
// Placeholder since we don't have the 3D icon
background: radial-gradient(circle at 50% 120%, rgba(0, 73, 180, 0.1), transparent 70%);
display: flex;
align-items: center;
justify-content: center;
@@ -312,6 +315,10 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
font-weight: 700;
color: #000;
margin: 0;
@include respond-to('sm') {
font-size: 29px;
}
}
&__desc {
@@ -323,6 +330,19 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
}
}
.service-keyImg {
width: 100%;
height: 100%;
object-fit: contain;
@include respond-to('sm') {
width: 90%;
height: 90%;
}
}
// =============================================================================
// 회원가입 안내 페이지 V2 (New Figma Design)
// =============================================================================
@@ -498,67 +518,75 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
display: flex;
justify-content: center;
margin-top: 20px;
&__btn {
display: inline-block;
background: #2a69de;
color: #fff;
font-size: 20px;
font-weight: 700;
padding: 16px 48px;
border-radius: 10px;
text-decoration: none;
transition: background 0.2s, transform 0.2s;
&:hover {
background: darken(#2a69de, 10%);
transform: translateY(-2px);
}
}
}
}
// Responsive
@media (max-width: 1024px) {
.signup-guide-v2 {
.signup-hero__inner {
.service-hero {
padding: 40px 0;
}
.service-hero__inner {
flex-direction: column;
gap: 30px;
text-align: center;
}
.signup-hero__content {
.service-hero__content {
align-items: center;
}
.signup-info-box {
flex-direction: column;
padding: 20px;
gap: 15px;
text-align: center;
ul li {
text-align: left;
}
}
}
}
@media (max-width: 768px) {
.signup-guide-v2 {
.signup-step {
gap: 20px;
gap: 15px;
&__icon-box {
width: 70px;
height: 70px;
width: 60px;
height: 60px;
svg {
width: 32px;
height: 32px;
width: 24px;
height: 24px;
}
}
&__card {
height: 70px;
padding: 0 20px;
height: auto;
min-height: 60px;
padding: 10px 15px;
&-title {
font-size: 15px;
}
}
}
.signup-timeline::before {
// left: 17px; is handled globally now
.signup-timeline {
padding-left: 50px;
&::before {
left: 17px;
}
}
.signup-step::before {
left: -41px; // adjusted for new padding
}
}
}
@@ -811,13 +839,20 @@ $o2leg-err-fg: #a23b3b;
}
.oauth2-2legged {
max-width: 1240px;
margin: 0 auto;
background-color: #FFFFFF;
color: $o2leg-text-dark;
display: flex;
flex-direction: column;
// max-width: 1240px;
// margin: 0 auto;
// background-color: #FFFFFF;
// color: $o2leg-text-dark;
// display: flex;
// flex-direction: column;
// box-sizing: border-box;
box-sizing: border-box;
color: $o2leg-text-dark;
*, *::before, *::after {
box-sizing: inherit;
}
&__prereq,
&__sequence,
@@ -979,6 +1014,8 @@ $o2leg-err-fg: #a23b3b;
background: #FFFFFF;
border: 1px solid $o2leg-border;
border-radius: 14px;
word-break: keep-all;
overflow-wrap: break-word;
}
&__prereq-num {
@@ -1019,10 +1056,13 @@ $o2leg-err-fg: #a23b3b;
background: #FFFFFF;
border: 1px solid $o2leg-border;
border-radius: 14px;
overflow-x: auto; // Prevent layout break, scroll on tiny screens
-webkit-overflow-scrolling: touch;
svg {
display: block;
width: 100%;
min-width: 580px; // Maintain readability on small viewports
height: auto;
}
}
@@ -1061,6 +1101,7 @@ $o2leg-err-fg: #a23b3b;
font-family: 'Fira Code', monospace;
font-size: 16px;
color: $o2leg-text-dark;
word-break: break-all;
}
&__endpoint-content-type {
@@ -1080,6 +1121,9 @@ $o2leg-err-fg: #a23b3b;
background: #FFFFFF;
border: 1px solid $o2leg-border;
border-radius: 12px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
min-width: 0;
}
&__panel-title {
@@ -1098,6 +1142,7 @@ $o2leg-err-fg: #a23b3b;
&__table {
width: 100%;
min-width: 600px;
border-collapse: collapse;
font-size: 13px;
@@ -1191,6 +1236,7 @@ $o2leg-err-fg: #a23b3b;
border-radius: 12px;
background: $o2leg-text-dark;
overflow: hidden;
min-width: 0;
&::before {
content: "";
@@ -1231,7 +1277,8 @@ $o2leg-err-fg: #a23b3b;
font-size: 13px;
line-height: 1.7;
white-space: pre;
overflow-x: auto;
overflow-x: auto; // 긴 코드는 코드 블록 내에서 스크롤
display: block; // pre 요소가 제대로 overflow 적용되도록
}
// syntax color spans (inside <pre>)
@@ -1261,6 +1308,10 @@ $o2leg-err-fg: #a23b3b;
border-radius: 8px;
background: $o2leg-bg-light;
margin-bottom: 6px;
display: flex;
flex-wrap: wrap; // Wrap key-value pairs
gap: 8px 12px;
align-items: center;
}
&__header-key {
@@ -1452,7 +1503,6 @@ $o2leg-err-fg: #a23b3b;
}
&__table--errors {
td:first-child,
td:nth-child(2),
td:nth-child(3) {
@@ -1464,7 +1514,6 @@ $o2leg-err-fg: #a23b3b;
@media (max-width: 640px) {
.oauth2-2legged {
padding: 16px;
gap: 16px;
&__hero {
@@ -10,8 +10,7 @@
<section class="service-hero">
<div class="service-hero__inner">
<div class="service-hero__icon-wrapper">
<img th:src="@{/img/keyimage/signUp_img.svg}" alt="회원가입 소개 3D 아이콘"
style="width: 100%; height: 100%; object-fit: contain;" />
<img th:src="@{/img/keyimage/signUp_img.png}" alt="회원가입 소개 3D 아이콘" class="service-keyImg" />
</div>
<div class="service-hero__content">
<div class="service-hero__badge">
@@ -26,12 +25,12 @@
</section>
<!-- Main Content -->
<div class="service-main">
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('guide')}"></th:block>
<!-- Content -->
<section class="service-content">
<section class=" service-content">
<!-- Info Box -->
<div class="signup-info-box">
<div class="signup-info-box__icon">
@@ -161,16 +160,18 @@
<!-- Step 5 -->
<div class="signup-step">
<div class="signup-step__icon-box five">
<!-- <svg width="40" height="40" viewBox="0 0 32 32" fill="none"-->
<!-- xmlns="http://www.w3.org/2000/svg">-->
<!-- <rect x="7" y="7" width="18" height="18" rx="2" stroke="#0049B4" stroke-width="1.6"-->
<!-- stroke-linecap="round" stroke-linejoin="round" />-->
<!-- <path d="M12 11H20" stroke="#0049B4" stroke-width="1.6" stroke-linecap="round" />-->
<!-- <path d="M12 15L14.5 17.5L20 12" stroke="#0049B4" stroke-width="1.6"-->
<!-- stroke-linecap="round" stroke-linejoin="round" />-->
<!-- </svg>-->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-ui-checks-grid" viewBox="0 0 16 16">
<path d="M2 10h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m9-9h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1m0 9a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1zm0-10a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h3a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zM2 9a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h3a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2zm7 2a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2zM0 2a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5.354.854a.5.5 0 1 0-.708-.708L3 3.793l-.646-.647a.5.5 0 1 0-.708.708l1 1a.5.5 0 0 0 .708 0z"/>
<!-- <svg width="40" height="40" viewBox="0 0 32 32" fill="none"-->
<!-- xmlns="http://www.w3.org/2000/svg">-->
<!-- <rect x="7" y="7" width="18" height="18" rx="2" stroke="#0049B4" stroke-width="1.6"-->
<!-- stroke-linecap="round" stroke-linejoin="round" />-->
<!-- <path d="M12 11H20" stroke="#0049B4" stroke-width="1.6" stroke-linecap="round" />-->
<!-- <path d="M12 15L14.5 17.5L20 12" stroke="#0049B4" stroke-width="1.6"-->
<!-- stroke-linecap="round" stroke-linejoin="round" />-->
<!-- </svg>-->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-ui-checks-grid" viewBox="0 0 16 16">
<path
d="M2 10h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m9-9h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1m0 9a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1zm0-10a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h3a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zM2 9a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h3a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2zm7 2a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2zM0 2a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5.354.854a.5.5 0 1 0-.708-.708L3 3.793l-.646-.647a.5.5 0 1 0-.708.708l1 1a.5.5 0 0 0 .708 0z" />
</svg>
</div>
<div class="signup-step__card">
@@ -206,7 +207,7 @@
</div>
<div class="signup-action">
<a th:href="@{/signup}" class="signup-action__btn">회원가입하러 가기</a>
<a th:href="@{/signup}" class="btn-action-primary">회원가입하러 가기</a>
</div>
</section>
</div>
@@ -1,196 +1,228 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
<body>
<th:block layout:fragment="contentFragment">
<section class="oauth2-2legged">
<th:block layout:fragment="contentFragment">
<section class="oauth2-2legged">
<!-- Section 1: Hero -->
<header class="oauth2-2legged__hero">
<div class="oauth2-2legged__hero-inner">
<div class="oauth2-2legged__hero-illust" aria-hidden="true">
<svg viewBox="0 0 280 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Server-to-Server Token Flow">
<defs>
<marker id="o2leg-hero-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#0049b4"/>
</marker>
</defs>
<rect x="0" y="0" width="280" height="200" rx="20" fill="#FFFFFF"/>
<!-- Section 1: Hero -->
<header class="oauth2-2legged__hero">
<div class="oauth2-2legged__hero-inner">
<div class="oauth2-2legged__hero-illust" aria-hidden="true">
<svg viewBox="0 0 280 200" xmlns="http://www.w3.org/2000/svg" role="img"
aria-label="Server-to-Server Token Flow">
<defs>
<marker id="o2leg-hero-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8"
markerHeight="8" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#0049b4" />
</marker>
</defs>
<rect x="0" y="0" width="280" height="200" rx="20" fill="#FFFFFF" />
<rect x="40" y="90" width="80" height="50" rx="8" fill="#EDF9FE" stroke="#0049b4"/>
<text x="80" y="120" text-anchor="middle" font-size="11" font-weight="700" fill="#0049b4">Client</text>
<rect x="40" y="90" width="80" height="50" rx="8" fill="#EDF9FE" stroke="#0049b4" />
<text x="80" y="120" text-anchor="middle" font-size="11" font-weight="700"
fill="#0049b4">Client</text>
<rect x="160" y="90" width="80" height="50" rx="8" fill="#EDF9FE" stroke="#0049b4"/>
<text x="200" y="112" text-anchor="middle" font-size="10" font-weight="700" fill="#0049b4">DJBank</text>
<text x="200" y="126" text-anchor="middle" font-size="10" font-weight="700" fill="#0049b4">Open API</text>
<rect x="160" y="90" width="80" height="50" rx="8" fill="#EDF9FE" stroke="#0049b4" />
<text x="200" y="112" text-anchor="middle" font-size="10" font-weight="700"
fill="#0049b4">DJBank</text>
<text x="200" y="126" text-anchor="middle" font-size="10" font-weight="700"
fill="#0049b4">Open API</text>
<line x1="120" y1="115" x2="160" y2="115" stroke="#0049b4" stroke-width="2" marker-end="url(#o2leg-hero-arrow)"/>
<line x1="120" y1="115" x2="160" y2="115" stroke="#0049b4" stroke-width="2"
marker-end="url(#o2leg-hero-arrow)" />
<g transform="translate(60,30)">
<rect width="160" height="32" rx="6" fill="#1A1A2E"/>
<text x="14" y="21" font-family="'Fira Code', monospace" font-size="12" fill="#00D4FF">Bearer eyJhbG..</text>
</g>
<g transform="translate(60,30)">
<rect width="160" height="32" rx="6" fill="#1A1A2E" />
<text x="14" y="21" font-family="'Fira Code', monospace" font-size="12"
fill="#00D4FF">Bearer eyJhbG..</text>
</g>
<text x="140" y="170" text-anchor="middle" font-size="11" font-weight="600" fill="#64748B">Server-to-Server Token Flow</text>
</svg>
</div>
<div class="oauth2-2legged__hero-body">
<span class="oauth2-2legged__hero-eyebrow">
<span class="oauth2-2legged__hero-eyebrow-dot"></span>
개발 가이드 · 2-Legged · Client Credentials
</span>
<h1 class="oauth2-2legged__hero-title">OAuth2 개발가이드</h1>
<p class="oauth2-2legged__hero-lead">DJBank Open API를 호출하기 위한 ClientID/Secret 기반 토큰 발급과<br>Bearer 인증 호출 방법을 단계별로 설명합니다.</p>
<!-- <div class="oauth2-2legged__hero-chips">-->
<!-- <span class="oauth2-2legged__chip oauth2-2legged__chip&#45;&#45;primary">Server-to-Server</span>-->
<!-- <span class="oauth2-2legged__chip">cURL 예제</span>-->
<!-- <span class="oauth2-2legged__chip">application/x-www-form</span>-->
<!-- </div>-->
</div>
</div>
</header>
<div class="service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('oauth2')}"></th:block>
<div class="service-content">
<!-- Section 2: 사전 준비 -->
<section class="oauth2-2legged__prereq" aria-labelledby="o2leg-prereq-title">
<span class="oauth2-2legged__eyebrow">PREREQUISITE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-prereq-title">시작 전 준비 사항</h2>
<div class="oauth2-2legged__prereq-grid">
<article class="oauth2-2legged__prereq-card">
<span class="oauth2-2legged__prereq-num">1</span>
<div class="oauth2-2legged__prereq-body">
<h3 class="oauth2-2legged__prereq-title">앱 등록</h3>
<p>개발자포털 [내 앱] 메뉴에서 신규</p>
<p>애플리케이션을 등록합니다.</p>
</div>
</article>
<article class="oauth2-2legged__prereq-card">
<span class="oauth2-2legged__prereq-num">2</span>
<div class="oauth2-2legged__prereq-body">
<h3 class="oauth2-2legged__prereq-title">ClientID / Secret 발급</h3>
<p>승인 완료 후 발급된 자격증명을</p>
<p>서버 환경변수에 안전하게 보관.</p>
</div>
</article>
<article class="oauth2-2legged__prereq-card">
<span class="oauth2-2legged__prereq-num">3</span>
<div class="oauth2-2legged__prereq-body">
<h3 class="oauth2-2legged__prereq-title">Scope 확인</h3>
<p>호출하려는 API에 필요한 scope</p>
<p>권한이 부여됐는지 확인합니다.</p>
</div>
</article>
</div>
</section>
<!-- Section 3: 전체 인증·호출 시퀀스 -->
<section class="oauth2-2legged__sequence" aria-labelledby="o2leg-seq-title">
<span class="oauth2-2legged__eyebrow">SEQUENCE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-seq-title">전체 인증·호출 시퀀스</h2>
<div class="oauth2-2legged__sequence-diagram">
<svg viewBox="0 0 1120 282" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OAuth 2.0 2-Legged Sequence">
<defs>
<marker id="o2leg-arrow-primary" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#0049b4"/>
</marker>
<marker id="o2leg-arrow-gray" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#64748B"/>
</marker>
</defs>
<g>
<rect x="120" y="24" width="240" height="48" rx="24" fill="#EDF9FE" stroke="#0049b4"/>
<text x="240" y="54" text-anchor="middle" font-size="14" font-weight="700" fill="#0049b4">Client Application</text>
<line x1="240" y1="72" x2="240" y2="254" stroke="#94A3B8" stroke-dasharray="4 4"/>
</g>
<g>
<rect x="760" y="24" width="240" height="48" rx="24" fill="#FFFFFF" stroke="#0049b4"/>
<text x="880" y="54" text-anchor="middle" font-size="14" font-weight="700" fill="#0049b4">DJBank Open API</text>
<line x1="880" y1="72" x2="880" y2="254" stroke="#94A3B8" stroke-dasharray="4 4"/>
</g>
<text x="560" y="102" text-anchor="middle" font-size="12" font-weight="700" fill="#0049b4">① POST /dj/oauth/token</text>
<text x="560" y="118" text-anchor="middle" font-size="11" font-weight="500" fill="#64748B">grant_type=client_credentials, client_id, client_secret, scope</text>
<line x1="240" y1="128" x2="880" y2="128" stroke="#0049b4" stroke-width="2" marker-end="url(#o2leg-arrow-primary)"/>
<text x="560" y="160" text-anchor="middle" font-size="12" font-weight="700" fill="#64748B">② access_token 발급</text>
<line x1="880" y1="170" x2="240" y2="170" stroke="#64748B" stroke-width="2" marker-end="url(#o2leg-arrow-gray)"/>
<text x="560" y="188" text-anchor="middle" font-size="11" font-weight="500" fill="#64748B">{ access_token, token_type:"bearer", expires_in:86400, scope, jti }</text>
<text x="560" y="216" text-anchor="middle" font-size="12" font-weight="700" fill="#0049b4">③ GET /api/v1/... · Authorization: Bearer &lt;access_token&gt;</text>
<line x1="240" y1="226" x2="880" y2="226" stroke="#0049b4" stroke-width="2" marker-end="url(#o2leg-arrow-primary)"/>
<text x="560" y="248" text-anchor="middle" font-size="12" font-weight="700" fill="#64748B">④ 검증 후 API 응답</text>
<line x1="880" y1="254" x2="240" y2="254" stroke="#64748B" stroke-width="2" marker-end="url(#o2leg-arrow-gray)"/>
</svg>
</div>
</section>
<!-- Section 4: STEP 1 — 액세스 토큰 요청 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step1-title">
<span class="oauth2-2legged__eyebrow">STEP 1</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step1-title">액세스 토큰 요청</h2>
<p class="oauth2-2legged__desc">ClientID/Secret 으로 토큰 엔드포인트에 POST 합니다.</p>
<div class="oauth2-2legged__endpoint-box">
<span class="oauth2-2legged__method">POST</span>
<code class="oauth2-2legged__endpoint-path">https://openapi.djbank.co.kr/dj/oauth/token</code>
<span class="oauth2-2legged__endpoint-content-type">x-www-form-urlencoded</span>
</div>
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">요청 파라미터</h3>
<table class="oauth2-2legged__table">
<thead>
<tr>
<th>PARAMETER</th>
<th>REQ</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>grant_type</code></td>
<td><span class="oauth2-2legged__req-badge oauth2-2legged__req-badge--required">필수</span></td>
<td>고정값 "client_credentials"</td>
</tr>
<tr>
<td><code>client_id</code></td>
<td><span class="oauth2-2legged__req-badge oauth2-2legged__req-badge--required">필수</span></td>
<td>발급받은 클라이언트 식별자</td>
</tr>
<tr>
<td><code>client_secret</code></td>
<td><span class="oauth2-2legged__req-badge oauth2-2legged__req-badge--required">필수</span></td>
<td>발급받은 비밀 키 (서버 보관)</td>
</tr>
<tr>
<td><code>scope</code></td>
<td><span class="oauth2-2legged__req-badge oauth2-2legged__req-badge--optional">선택</span></td>
<td>호출 권한 범위 (공백 구분)</td>
</tr>
</tbody>
</table>
<p class="oauth2-2legged__warning">⚠ Content-Type: application/x-www-form-urlencoded 헤더 필수</p>
<text x="140" y="170" text-anchor="middle" font-size="11" font-weight="600"
fill="#64748B">Server-to-Server Token Flow</text>
</svg>
</div>
<div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag">cURL</span>
<pre class="oauth2-2legged__code-block"><span class="o2leg-cm"># 액세스 토큰 발급</span>
<div class="oauth2-2legged__hero-body">
<span class="oauth2-2legged__hero-eyebrow">
<span class="oauth2-2legged__hero-eyebrow-dot"></span>
개발 가이드 · 2-Legged · Client Credentials
</span>
<h1 class="oauth2-2legged__hero-title">OAuth2 개발가이드</h1>
<p class="oauth2-2legged__hero-lead">DJBank Open API를 호출하기 위한 ClientID/Secret 기반 토큰
발급과<br>Bearer 인증 호출 방법을 단계별로 설명합니다.</p>
</div>
</div>
</header>
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('oauth2')}"></th:block>
<div class="service-content">
<!-- Section 2: 사전 준비 -->
<section class="oauth2-2legged__prereq" aria-labelledby="o2leg-prereq-title">
<span class="oauth2-2legged__eyebrow">PREREQUISITE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-prereq-title">시작 전 준비 사항</h2>
<div class="oauth2-2legged__prereq-grid">
<article class="oauth2-2legged__prereq-card">
<span class="oauth2-2legged__prereq-num">1</span>
<div class="oauth2-2legged__prereq-body">
<h3 class="oauth2-2legged__prereq-title">앱 등록</h3>
<p>개발자포털 [내 앱] 메뉴에서 신규</p>
<p>애플리케이션을 등록합니다.</p>
</div>
</article>
<article class="oauth2-2legged__prereq-card">
<span class="oauth2-2legged__prereq-num">2</span>
<div class="oauth2-2legged__prereq-body">
<h3 class="oauth2-2legged__prereq-title">ClientID / Secret 발급</h3>
<p>승인 완료 후 발급된 자격증명을</p>
<p>서버 환경변수에 안전하게 보관.</p>
</div>
</article>
<article class="oauth2-2legged__prereq-card">
<span class="oauth2-2legged__prereq-num">3</span>
<div class="oauth2-2legged__prereq-body">
<h3 class="oauth2-2legged__prereq-title">Scope 확인</h3>
<p>호출하려는 API에 필요한 scope</p>
<p>권한이 부여됐는지 확인합니다.</p>
</div>
</article>
</div>
</section>
<!-- Section 3: 전체 인증·호출 시퀀스 -->
<section class="oauth2-2legged__sequence" aria-labelledby="o2leg-seq-title">
<span class="oauth2-2legged__eyebrow">SEQUENCE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-seq-title">전체 인증·호출 시퀀스</h2>
<div class="oauth2-2legged__sequence-diagram">
<svg viewBox="0 0 1120 282" xmlns="http://www.w3.org/2000/svg" role="img"
aria-label="OAuth 2.0 2-Legged Sequence">
<defs>
<marker id="o2leg-arrow-primary" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="8" markerHeight="8" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#0049b4" />
</marker>
<marker id="o2leg-arrow-gray" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8"
markerHeight="8" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#64748B" />
</marker>
</defs>
<g>
<rect x="120" y="24" width="240" height="48" rx="24" fill="#EDF9FE"
stroke="#0049b4" />
<text x="240" y="54" text-anchor="middle" font-size="14" font-weight="700"
fill="#0049b4">Client Application</text>
<line x1="240" y1="72" x2="240" y2="254" stroke="#94A3B8" stroke-dasharray="4 4" />
</g>
<g>
<rect x="760" y="24" width="240" height="48" rx="24" fill="#FFFFFF"
stroke="#0049b4" />
<text x="880" y="54" text-anchor="middle" font-size="14" font-weight="700"
fill="#0049b4">DJBank Open API</text>
<line x1="880" y1="72" x2="880" y2="254" stroke="#94A3B8" stroke-dasharray="4 4" />
</g>
<text x="560" y="102" text-anchor="middle" font-size="12" font-weight="700"
fill="#0049b4">① POST /dj/oauth/token</text>
<text x="560" y="118" text-anchor="middle" font-size="11" font-weight="500"
fill="#64748B">grant_type=client_credentials, client_id, client_secret, scope</text>
<line x1="240" y1="128" x2="880" y2="128" stroke="#0049b4" stroke-width="2"
marker-end="url(#o2leg-arrow-primary)" />
<text x="560" y="160" text-anchor="middle" font-size="12" font-weight="700"
fill="#64748B">② access_token 발급</text>
<line x1="880" y1="170" x2="240" y2="170" stroke="#64748B" stroke-width="2"
marker-end="url(#o2leg-arrow-gray)" />
<text x="560" y="188" text-anchor="middle" font-size="11" font-weight="500"
fill="#64748B">{ access_token, token_type:"bearer", expires_in:86400, scope, jti
}</text>
<text x="560" y="216" text-anchor="middle" font-size="12" font-weight="700"
fill="#0049b4">③ GET /api/v1/... · Authorization: Bearer &lt;access_token&gt;</text>
<line x1="240" y1="226" x2="880" y2="226" stroke="#0049b4" stroke-width="2"
marker-end="url(#o2leg-arrow-primary)" />
<text x="560" y="248" text-anchor="middle" font-size="12" font-weight="700"
fill="#64748B">④ 검증 후 API 응답</text>
<line x1="880" y1="254" x2="240" y2="254" stroke="#64748B" stroke-width="2"
marker-end="url(#o2leg-arrow-gray)" />
</svg>
</div>
</section>
<!-- Section 4: STEP 1 — 액세스 토큰 요청 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step1-title">
<span class="oauth2-2legged__eyebrow">STEP 1</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step1-title">액세스 토큰 요청</h2>
<p class="oauth2-2legged__desc">ClientID/Secret 으로 토큰 엔드포인트에 POST 합니다.</p>
<div class="oauth2-2legged__endpoint-box">
<span class="oauth2-2legged__method">POST</span>
<code
class="oauth2-2legged__endpoint-path">https://openapi.djbank.co.kr/dj/oauth/token</code>
<span class="oauth2-2legged__endpoint-content-type">x-www-form-urlencoded</span>
</div>
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">요청 파라미터</h3>
<table class="oauth2-2legged__table">
<thead>
<tr>
<th>PARAMETER</th>
<th>REQ</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>grant_type</code></td>
<td><span
class="oauth2-2legged__req-badge oauth2-2legged__req-badge--required">필수</span>
</td>
<td>고정값 "client_credentials"</td>
</tr>
<tr>
<td><code>client_id</code></td>
<td><span
class="oauth2-2legged__req-badge oauth2-2legged__req-badge--required">필수</span>
</td>
<td>발급받은 클라이언트 식별자</td>
</tr>
<tr>
<td><code>client_secret</code></td>
<td><span
class="oauth2-2legged__req-badge oauth2-2legged__req-badge--required">필수</span>
</td>
<td>발급받은 비밀 키 (서버 보관)</td>
</tr>
<tr>
<td><code>scope</code></td>
<td><span
class="oauth2-2legged__req-badge oauth2-2legged__req-badge--optional">선택</span>
</td>
<td>호출 권한 범위 (공백 구분)</td>
</tr>
</tbody>
</table>
<p class="oauth2-2legged__warning">⚠ Content-Type: application/x-www-form-urlencoded 헤더
필수</p>
</div>
<div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag">cURL</span>
<pre class="oauth2-2legged__code-block"><span class="o2leg-cm"># 액세스 토큰 발급</span>
curl -X <span class="o2leg-y">POST</span> <span class="o2leg-c">'https://openapi.djbank.co.kr/dj/oauth/token'</span> \
-H <span class="o2leg-c">'Content-Type: application/x-www-form-urlencoded'</span> \
-d <span class="o2leg-c">'grant_type=client_credentials'</span> \
@@ -199,20 +231,20 @@ curl -X <span class="o2leg-y">POST</span> <span class="o2leg-c">'https://openapi
-d <span class="o2leg-c">'scope=read.accounts'</span>
<span class="o2leg-g"># 응답: 200 OK + JSON</span></pre>
</div>
</div>
</section>
</div>
</div>
</section>
<!-- Section 5: STEP 2 — 액세스 토큰 응답 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step2-title">
<span class="oauth2-2legged__eyebrow">STEP 2</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step2-title">액세스 토큰 응답</h2>
<p class="oauth2-2legged__desc">정상 발급 시 200 OK 와 함께 다음 JSON 을 반환합니다.</p>
<!-- Section 5: STEP 2 — 액세스 토큰 응답 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step2-title">
<span class="oauth2-2legged__eyebrow">STEP 2</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step2-title">액세스 토큰 응답</h2>
<p class="oauth2-2legged__desc">정상 발급 시 200 OK 와 함께 다음 JSON 을 반환합니다.</p>
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--ok">200 OK · JSON</span>
<pre class="oauth2-2legged__code-block">{
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--ok">200 OK · JSON</span>
<pre class="oauth2-2legged__code-block">{
<span class="o2leg-c">"access_token"</span>: <span class="o2leg-y">"eyJhbGciOiJSUzI1NiJ9..."</span>,
<span class="o2leg-c">"token_type"</span>: <span class="o2leg-y">"bearer"</span>,
<span class="o2leg-c">"expires_in"</span>: <span class="o2leg-p">86400</span>,
@@ -221,76 +253,76 @@ curl -X <span class="o2leg-y">POST</span> <span class="o2leg-c">'https://openapi
}
<span class="o2leg-g"># 응답 본문은 캐싱하여 expires_in 동안 재사용 가능</span></pre>
</div>
</div>
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">응답 필드</h3>
<table class="oauth2-2legged__table">
<thead>
<tr>
<th>FIELD</th>
<th>TYPE</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="oauth2-2legged__field-name">access_token</code></td>
<td>string</td>
<td>Bearer 호출에 사용할 토큰</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">token_type</code></td>
<td>string</td>
<td>고정값 "bearer"</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">expires_in</code></td>
<td>int</td>
<td>유효 시간(초). 기본 86400</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">scope</code></td>
<td>string</td>
<td>실제 부여된 권한 범위</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">jti</code></td>
<td>string</td>
<td>토큰 고유 ID (감사/취소용)</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Section 6: STEP 3 — API 호출 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step3-title">
<span class="oauth2-2legged__eyebrow">STEP 3</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step3-title">발급 토큰으로 API 호출</h2>
<p class="oauth2-2legged__desc">Authorization 헤더에 Bearer 토큰을 실어 보호 자원 API 를 호출합니다.</p>
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">필수 헤더</h3>
<div class="oauth2-2legged__header-box">
<code class="oauth2-2legged__header-key">Authorization:</code>
<code class="oauth2-2legged__header-value">Bearer eyJhbGciOiJSUzI1NiJ9...</code>
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">응답 필드</h3>
<table class="oauth2-2legged__table">
<thead>
<tr>
<th>FIELD</th>
<th>TYPE</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="oauth2-2legged__field-name">access_token</code></td>
<td>string</td>
<td>Bearer 호출에 사용할 토큰</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">token_type</code></td>
<td>string</td>
<td>고정값 "bearer"</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">expires_in</code></td>
<td>int</td>
<td>유효 시간(초). 기본 86400</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">scope</code></td>
<td>string</td>
<td>실제 부여된 권한 범위</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">jti</code></td>
<td>string</td>
<td>토큰 고유 ID (감사/취소용)</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<h4 class="oauth2-2legged__panel-subtitle">토큰 재사용 가이드</h4>
<ul class="oauth2-2legged__tips">
<li>동일 토큰은 expires_in(기본 86400초) 동안 재사용</li>
<li>만료 임박 시 재발급 후 교체 (예: TTL의 80% 시점)</li>
<li>매 호출마다 토큰을 새로 발급하지 마세요</li>
<li>권한이 다른 API 는 scope 별로 토큰을 분리 발급</li>
</ul>
</div>
<!-- Section 6: STEP 3 — API 호출 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step3-title">
<span class="oauth2-2legged__eyebrow">STEP 3</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step3-title">발급 토큰으로 API 호출</h2>
<p class="oauth2-2legged__desc">Authorization 헤더에 Bearer 토큰을 실어 보호 자원 API 를 호출합니다.</p>
<div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag">cURL</span>
<pre class="oauth2-2legged__code-block"><span class="o2leg-cm"># 보호 자원 API 호출</span>
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">필수 헤더</h3>
<div class="oauth2-2legged__header-box">
<code class="oauth2-2legged__header-key">Authorization:</code>
<code class="oauth2-2legged__header-value">Bearer eyJhbGciOiJSUzI1NiJ9...</code>
</div>
<h4 class="oauth2-2legged__panel-subtitle">토큰 재사용 가이드</h4>
<ul class="oauth2-2legged__tips">
<li>동일 토큰은 expires_in(기본 86400초) 동안 재사용</li>
<li>만료 임박 시 재발급 후 교체 (예: TTL의 80% 시점)</li>
<li>매 호출마다 토큰을 새로 발급하지 마세요</li>
<li>권한이 다른 API 는 scope 별로 토큰을 분리 발급</li>
</ul>
</div>
<div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag">cURL</span>
<pre class="oauth2-2legged__code-block"><span class="o2leg-cm"># 보호 자원 API 호출</span>
curl -X <span class="o2leg-y">GET</span> \
<span class="o2leg-c">'https://openapi.djbank.co.kr/api/v1/accounts'</span> \
-H <span class="o2leg-c">'Authorization: Bearer eyJhbGciOiJSUzI..'</span> \
@@ -299,65 +331,74 @@ curl -X <span class="o2leg-y">GET</span> \
<span class="o2leg-g"># 응답</span>
HTTP/1.1 <span class="o2leg-g">200 OK</span>
{ <span class="o2leg-c">"accounts"</span>: [ ... ] }</pre>
</div>
</div>
</section>
</div>
</div>
</section>
<!-- Section 7: 대표 에러 응답 (표) -->
<section class="oauth2-2legged__errors" aria-labelledby="o2leg-errors-title">
<span class="oauth2-2legged__eyebrow">ERROR CODES</span>
<h2 class="oauth2-2legged__h2" id="o2leg-errors-title">대표 에러 응답</h2>
<!-- Section 7: 대표 에러 응답 (표) -->
<section class="oauth2-2legged__errors" aria-labelledby="o2leg-errors-title">
<span class="oauth2-2legged__eyebrow">ERROR CODES</span>
<h2 class="oauth2-2legged__h2" id="o2leg-errors-title">대표 에러 응답</h2>
<div class="oauth2-2legged__panel">
<table class="oauth2-2legged__table oauth2-2legged__table--errors">
<thead>
<tr>
<th>HTTP</th>
<th>ERROR CODE</th>
<th>의미</th>
<th>해결 가이드</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">400</span></td>
<td><code>invalid_request</code></td>
<td>필수 파라미터 누락/형식 오류</td>
<td>grant_type, client_id, client_secret 확인</td>
</tr>
<tr>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span></td>
<td><code>invalid_client</code></td>
<td>자격증명 불일치 / 미승인</td>
<td>ClientID·Secret 재확인, 앱 상태 점검</td>
</tr>
<tr>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span></td>
<td><code>invalid_token</code></td>
<td>만료/위·변조된 토큰</td>
<td>토큰 재발급 후 재시도</td>
</tr>
<tr>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--403">403</span></td>
<td><code>insufficient_scope</code></td>
<td>scope 권한 부족</td>
<td>필요 scope 추가 신청 후 재발급</td>
</tr>
</tbody>
</table>
</div>
</section>
<div class="oauth2-2legged__panel">
<table class="oauth2-2legged__table oauth2-2legged__table--errors">
<thead>
<tr>
<th>HTTP</th>
<th>ERROR CODE</th>
<th>의미</th>
<th>해결 가이드</th>
</tr>
</thead>
<tbody>
<tr>
<td><span
class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">400</span>
</td>
<td><code>invalid_request</code></td>
<td>필수 파라미터 누락/형식 오류</td>
<td>grant_type, client_id, client_secret 확인</td>
</tr>
<tr>
<td><span
class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span>
</td>
<td><code>invalid_client</code></td>
<td>자격증명 불일치 / 미승인</td>
<td>ClientID·Secret 재확인, 앱 상태 점검</td>
</tr>
<tr>
<td><span
class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span>
</td>
<td><code>invalid_token</code></td>
<td>만료/위·변조된 토큰</td>
<td>토큰 재발급 후 재시도</td>
</tr>
<tr>
<td><span
class="oauth2-2legged__status-badge oauth2-2legged__status-badge--403">403</span>
</td>
<td><code>insufficient_scope</code></td>
<td>scope 권한 부족</td>
<td>필요 scope 추가 신청 후 재발급</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Section 8: 에러 응답 예시 (단일 사례) -->
<section class="oauth2-2legged__error-example" aria-labelledby="o2leg-err-ex-title">
<span class="oauth2-2legged__eyebrow">ERROR RESPONSE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-err-ex-title">에러 응답 예시</h2>
<p class="oauth2-2legged__desc">자격증명 불일치 시 401 Unauthorized 와 함께 다음 JSON 을 반환합니다.</p>
<!-- Section 8: 에러 응답 예시 (단일 사례) -->
<section class="oauth2-2legged__error-example" aria-labelledby="o2leg-err-ex-title">
<span class="oauth2-2legged__eyebrow">ERROR RESPONSE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-err-ex-title">에러 응답 예시</h2>
<p class="oauth2-2legged__desc">자격증명 불일치 시 401 Unauthorized 와 함께 다음 JSON 을 반환합니다.</p>
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__code-panel oauth2-2legged__code-panel--error">
<span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--error">401 Unauthorized · JSON</span>
<pre class="oauth2-2legged__code-block">{
<div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__code-panel oauth2-2legged__code-panel--error">
<span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--error">401 Unauthorized
· JSON</span>
<pre class="oauth2-2legged__code-block">{
<span class="o2leg-c">"error"</span>: <span class="o2leg-y">"invalid_client"</span>,
<span class="o2leg-c">"error_description"</span>:
<span class="o2leg-y">"Client authentication failed"</span>,
@@ -366,66 +407,66 @@ HTTP/1.1 <span class="o2leg-g">200 OK</span>
}
<span class="o2leg-g"># 처리: ClientID·Secret 확인 후 재시도</span></pre>
</div>
</div>
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">응답 필드</h3>
<table class="oauth2-2legged__table">
<thead>
<tr>
<th>FIELD</th>
<th>TYPE</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="oauth2-2legged__field-name">error</code></td>
<td>string</td>
<td>에러 코드 (예: invalid_client)</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">error_description</code></td>
<td>string</td>
<td>사람이 읽을 수 있는 설명</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">status</code></td>
<td>int</td>
<td>HTTP 상태 코드</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">timestamp</code></td>
<td>string</td>
<td>에러 발생 시각 (ISO 8601)</td>
</tr>
</tbody>
</table>
<p class="oauth2-2legged__error-note">⚠ error 코드는 RFC 6749 표준 코드 또는 DJBank 확장 코드</p>
</div>
</div>
</section>
<!-- Section 9: API 목록 CTA -->
<a class="oauth2-2legged__cta" th:href="@{/apis}">
<div class="oauth2-2legged__cta-body">
<span class="oauth2-2legged__cta-eyebrow">EXPLORE</span>
<h2 class="oauth2-2legged__cta-title">API 목록 보러가기</h2>
<p class="oauth2-2legged__cta-desc">사용 가능한 DJBank Open API 카탈로그를 확인하세요.</p>
<span class="oauth2-2legged__cta-button">API 목록 →</span>
</div>
<span class="oauth2-2legged__cta-deco oauth2-2legged__cta-deco--lg" aria-hidden="true"></span>
<span class="oauth2-2legged__cta-deco oauth2-2legged__cta-deco--sm" aria-hidden="true"></span>
</a>
<div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">응답 필드</h3>
<table class="oauth2-2legged__table">
<thead>
<tr>
<th>FIELD</th>
<th>TYPE</th>
<th>설명</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="oauth2-2legged__field-name">error</code></td>
<td>string</td>
<td>에러 코드 (예: invalid_client)</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">error_description</code></td>
<td>string</td>
<td>사람이 읽을 수 있는 설명</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">status</code></td>
<td>int</td>
<td>HTTP 상태 코드</td>
</tr>
<tr>
<td><code class="oauth2-2legged__field-name">timestamp</code></td>
<td>string</td>
<td>에러 발생 시각 (ISO 8601)</td>
</tr>
</tbody>
</table>
<p class="oauth2-2legged__error-note">⚠ error 코드는 RFC 6749 표준 코드 또는 DJBank 확장 코드</p>
</div>
</div>
</section>
<!-- Section 9: API 목록 CTA -->
<a class="oauth2-2legged__cta" th:href="@{/apis}">
<div class="oauth2-2legged__cta-body">
<span class="oauth2-2legged__cta-eyebrow">EXPLORE</span>
<h2 class="oauth2-2legged__cta-title">API 목록 보러가기</h2>
<p class="oauth2-2legged__cta-desc">사용 가능한 DJBank Open API 카탈로그를 확인하세요.</p>
<span class="oauth2-2legged__cta-button">API 목록 →</span>
</div>
<span class="oauth2-2legged__cta-deco oauth2-2legged__cta-deco--lg" aria-hidden="true"></span>
<span class="oauth2-2legged__cta-deco oauth2-2legged__cta-deco--sm" aria-hidden="true"></span>
</a>
</div>
</div>
</section>
</th:block>
</section>
</th:block>
</body>
<th:block layout:fragment="contentScript">
</th:block>
</html>
</html>