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 { .status-badge {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -22807,9 +22833,7 @@ body.commission-print-page .btn-primary:hover {
} }
} }
.service-main { .service-main {
max-width: 1240px; padding: 60px 0;
margin: 0 auto;
padding: 60px 40px;
display: flex; display: flex;
gap: 60px; gap: 60px;
align-items: flex-start; align-items: flex-start;
@@ -22817,6 +22841,7 @@ body.commission-print-page .btn-primary:hover {
.service-content { .service-content {
flex: 1; flex: 1;
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 40px; gap: 40px;
@@ -22903,6 +22928,10 @@ body.commission-print-page .btn-primary:hover {
justify-content: center; justify-content: center;
padding: 20px; padding: 20px;
} }
.service-hero,
.oauth2-2legged__hero {
width: 100vw;
}
} }
.service-hero { .service-hero {
background: linear-gradient(to right, #eff9fe, #fcfeff); background: linear-gradient(to right, #eff9fe, #fcfeff);
@@ -22928,7 +22957,6 @@ body.commission-print-page .btn-primary:hover {
width: 230px; width: 230px;
height: 167px; height: 167px;
position: relative; position: relative;
background: radial-gradient(circle at 50% 120%, rgba(0, 73, 180, 0.1), transparent 70%);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -22967,6 +22995,11 @@ body.commission-print-page .btn-primary:hover {
color: #000; color: #000;
margin: 0; margin: 0;
} }
@media (max-width: 768px) {
.service-hero__title {
font-size: 29px;
}
}
.service-hero__desc { .service-hero__desc {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
@@ -22975,6 +23008,18 @@ body.commission-print-page .btn-primary:hover {
margin: 0; margin: 0;
} }
.service-keyImg {
width: 100%;
height: 100%;
object-fit: contain;
}
@media (max-width: 768px) {
.service-keyImg {
width: 90%;
height: 90%;
}
}
.signup-guide-v2 { .signup-guide-v2 {
background-color: #fff; background-color: #fff;
padding-bottom: 100px; padding-bottom: 100px;
@@ -23123,47 +23168,57 @@ body.commission-print-page .btn-primary:hover {
justify-content: center; justify-content: center;
margin-top: 20px; 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) { @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; flex-direction: column;
gap: 30px; gap: 30px;
text-align: center; text-align: center;
} }
.signup-guide-v2 .signup-hero__content { .signup-guide-v2 .service-hero__content {
align-items: center; 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) { @media (max-width: 768px) {
.signup-guide-v2 .signup-step { .signup-guide-v2 .signup-step {
gap: 20px; gap: 15px;
} }
.signup-guide-v2 .signup-step__icon-box { .signup-guide-v2 .signup-step__icon-box {
width: 70px; width: 60px;
height: 70px; height: 60px;
} }
.signup-guide-v2 .signup-step__icon-box svg { .signup-guide-v2 .signup-step__icon-box svg {
width: 32px; width: 24px;
height: 32px; height: 24px;
} }
.signup-guide-v2 .signup-step__card { .signup-guide-v2 .signup-step__card {
height: 70px; height: auto;
padding: 0 20px; 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 { .service_guide_header {
@@ -23343,13 +23398,11 @@ body.commission-print-page .btn-primary:hover {
} }
.oauth2-2legged { .oauth2-2legged {
max-width: 1240px;
margin: 0 auto;
background-color: #FFFFFF;
color: #1A1A2E;
display: flex;
flex-direction: column;
box-sizing: border-box; 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 { .oauth2-2legged__prereq, .oauth2-2legged__sequence, .oauth2-2legged__step, .oauth2-2legged__errors, .oauth2-2legged__error-example {
margin-bottom: 50px; margin-bottom: 50px;
@@ -23485,6 +23538,8 @@ body.commission-print-page .btn-primary:hover {
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #E2E8F0; border: 1px solid #E2E8F0;
border-radius: 14px; border-radius: 14px;
word-break: keep-all;
overflow-wrap: break-word;
} }
.oauth2-2legged__prereq-num { .oauth2-2legged__prereq-num {
display: inline-flex; display: inline-flex;
@@ -23519,10 +23574,13 @@ body.commission-print-page .btn-primary:hover {
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #E2E8F0; border: 1px solid #E2E8F0;
border-radius: 14px; border-radius: 14px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
} }
.oauth2-2legged__sequence-diagram svg { .oauth2-2legged__sequence-diagram svg {
display: block; display: block;
width: 100%; width: 100%;
min-width: 580px;
height: auto; height: auto;
} }
.oauth2-2legged__step-grid { .oauth2-2legged__step-grid {
@@ -23555,6 +23613,7 @@ body.commission-print-page .btn-primary:hover {
font-family: "Fira Code", monospace; font-family: "Fira Code", monospace;
font-size: 16px; font-size: 16px;
color: #1A1A2E; color: #1A1A2E;
word-break: break-all;
} }
.oauth2-2legged__endpoint-content-type { .oauth2-2legged__endpoint-content-type {
display: inline-block; display: inline-block;
@@ -23571,6 +23630,9 @@ body.commission-print-page .btn-primary:hover {
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #E2E8F0; border: 1px solid #E2E8F0;
border-radius: 12px; border-radius: 12px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
min-width: 0;
} }
.oauth2-2legged__panel-title { .oauth2-2legged__panel-title {
margin: 0 0 14px; margin: 0 0 14px;
@@ -23586,6 +23648,7 @@ body.commission-print-page .btn-primary:hover {
} }
.oauth2-2legged__table { .oauth2-2legged__table {
width: 100%; width: 100%;
min-width: 600px;
border-collapse: collapse; border-collapse: collapse;
font-size: 13px; font-size: 13px;
} }
@@ -23661,6 +23724,7 @@ body.commission-print-page .btn-primary:hover {
border-radius: 12px; border-radius: 12px;
background: #1A1A2E; background: #1A1A2E;
overflow: hidden; overflow: hidden;
min-width: 0;
} }
.oauth2-2legged__code-panel::before { .oauth2-2legged__code-panel::before {
content: ""; content: "";
@@ -23696,6 +23760,7 @@ body.commission-print-page .btn-primary:hover {
line-height: 1.7; line-height: 1.7;
white-space: pre; white-space: pre;
overflow-x: auto; overflow-x: auto;
display: block;
} }
.oauth2-2legged .o2leg-c { .oauth2-2legged .o2leg-c {
color: #00D4FF; color: #00D4FF;
@@ -23717,6 +23782,10 @@ body.commission-print-page .btn-primary:hover {
border-radius: 8px; border-radius: 8px;
background: #F8FAFC; background: #F8FAFC;
margin-bottom: 6px; margin-bottom: 6px;
display: flex;
flex-wrap: wrap;
gap: 8px 12px;
align-items: center;
} }
.oauth2-2legged__header-key { .oauth2-2legged__header-key {
display: block; display: block;
@@ -23884,7 +23953,6 @@ body.commission-print-page .btn-primary:hover {
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.oauth2-2legged { .oauth2-2legged {
padding: 16px;
gap: 16px; gap: 16px;
} }
.oauth2-2legged__hero { .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; 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 { .service-main {
max-width: 1240px; padding: 60px 0;
margin: 0 auto;
padding: 60px 40px;
display: flex; display: flex;
gap: 60px; gap: 60px;
align-items: flex-start; 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 { .service-content {
flex: 1; flex: 1;
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 40px; 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; justify-content: center;
padding: 20px; 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; width: 230px;
height: 167px; height: 167px;
position: relative; 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; display: flex;
align-items: center; align-items: center;
justify-content: 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; font-weight: 700;
color: #000; color: #000;
margin: 0; margin: 0;
@include respond-to('sm') {
font-size: 29px;
}
} }
&__desc { &__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) // 회원가입 안내 페이지 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; display: flex;
justify-content: center; justify-content: center;
margin-top: 20px; 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 // Responsive
@media (max-width: 1024px) { @media (max-width: 1024px) {
.signup-guide-v2 { .signup-guide-v2 {
.signup-hero__inner { .service-hero {
padding: 40px 0;
}
.service-hero__inner {
flex-direction: column; flex-direction: column;
gap: 30px; gap: 30px;
text-align: center; text-align: center;
} }
.signup-hero__content { .service-hero__content {
align-items: center; 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) { @media (max-width: 768px) {
.signup-guide-v2 { .signup-guide-v2 {
.signup-step { .signup-step {
gap: 20px; gap: 15px;
&__icon-box { &__icon-box {
width: 70px; width: 60px;
height: 70px; height: 60px;
svg { svg {
width: 32px; width: 24px;
height: 32px; height: 24px;
} }
} }
&__card { &__card {
height: 70px; height: auto;
padding: 0 20px; min-height: 60px;
padding: 10px 15px;
&-title {
font-size: 15px;
}
} }
} }
.signup-timeline::before { .signup-timeline {
// left: 17px; is handled globally now 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 { .oauth2-2legged {
max-width: 1240px; // max-width: 1240px;
margin: 0 auto; // margin: 0 auto;
background-color: #FFFFFF; // background-color: #FFFFFF;
color: $o2leg-text-dark; // color: $o2leg-text-dark;
display: flex; // display: flex;
flex-direction: column; // flex-direction: column;
// box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
color: $o2leg-text-dark;
*, *::before, *::after {
box-sizing: inherit;
}
&__prereq, &__prereq,
&__sequence, &__sequence,
@@ -979,6 +1014,8 @@ $o2leg-err-fg: #a23b3b;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid $o2leg-border; border: 1px solid $o2leg-border;
border-radius: 14px; border-radius: 14px;
word-break: keep-all;
overflow-wrap: break-word;
} }
&__prereq-num { &__prereq-num {
@@ -1019,10 +1056,13 @@ $o2leg-err-fg: #a23b3b;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid $o2leg-border; border: 1px solid $o2leg-border;
border-radius: 14px; border-radius: 14px;
overflow-x: auto; // Prevent layout break, scroll on tiny screens
-webkit-overflow-scrolling: touch;
svg { svg {
display: block; display: block;
width: 100%; width: 100%;
min-width: 580px; // Maintain readability on small viewports
height: auto; height: auto;
} }
} }
@@ -1061,6 +1101,7 @@ $o2leg-err-fg: #a23b3b;
font-family: 'Fira Code', monospace; font-family: 'Fira Code', monospace;
font-size: 16px; font-size: 16px;
color: $o2leg-text-dark; color: $o2leg-text-dark;
word-break: break-all;
} }
&__endpoint-content-type { &__endpoint-content-type {
@@ -1080,6 +1121,9 @@ $o2leg-err-fg: #a23b3b;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid $o2leg-border; border: 1px solid $o2leg-border;
border-radius: 12px; border-radius: 12px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
min-width: 0;
} }
&__panel-title { &__panel-title {
@@ -1098,6 +1142,7 @@ $o2leg-err-fg: #a23b3b;
&__table { &__table {
width: 100%; width: 100%;
min-width: 600px;
border-collapse: collapse; border-collapse: collapse;
font-size: 13px; font-size: 13px;
@@ -1191,6 +1236,7 @@ $o2leg-err-fg: #a23b3b;
border-radius: 12px; border-radius: 12px;
background: $o2leg-text-dark; background: $o2leg-text-dark;
overflow: hidden; overflow: hidden;
min-width: 0;
&::before { &::before {
content: ""; content: "";
@@ -1231,7 +1277,8 @@ $o2leg-err-fg: #a23b3b;
font-size: 13px; font-size: 13px;
line-height: 1.7; line-height: 1.7;
white-space: pre; white-space: pre;
overflow-x: auto; overflow-x: auto; // 긴 코드는 코드 블록 내에서 스크롤
display: block; // pre 요소가 제대로 overflow 적용되도록
} }
// syntax color spans (inside <pre>) // syntax color spans (inside <pre>)
@@ -1261,6 +1308,10 @@ $o2leg-err-fg: #a23b3b;
border-radius: 8px; border-radius: 8px;
background: $o2leg-bg-light; background: $o2leg-bg-light;
margin-bottom: 6px; margin-bottom: 6px;
display: flex;
flex-wrap: wrap; // Wrap key-value pairs
gap: 8px 12px;
align-items: center;
} }
&__header-key { &__header-key {
@@ -1452,7 +1503,6 @@ $o2leg-err-fg: #a23b3b;
} }
&__table--errors { &__table--errors {
td:first-child, td:first-child,
td:nth-child(2), td:nth-child(2),
td:nth-child(3) { td:nth-child(3) {
@@ -1464,7 +1514,6 @@ $o2leg-err-fg: #a23b3b;
@media (max-width: 640px) { @media (max-width: 640px) {
.oauth2-2legged { .oauth2-2legged {
padding: 16px;
gap: 16px; gap: 16px;
&__hero { &__hero {
@@ -10,8 +10,7 @@
<section class="service-hero"> <section class="service-hero">
<div class="service-hero__inner"> <div class="service-hero__inner">
<div class="service-hero__icon-wrapper"> <div class="service-hero__icon-wrapper">
<img th:src="@{/img/keyimage/signUp_img.svg}" alt="회원가입 소개 3D 아이콘" <img th:src="@{/img/keyimage/signUp_img.png}" alt="회원가입 소개 3D 아이콘" class="service-keyImg" />
style="width: 100%; height: 100%; object-fit: contain;" />
</div> </div>
<div class="service-hero__content"> <div class="service-hero__content">
<div class="service-hero__badge"> <div class="service-hero__badge">
@@ -26,12 +25,12 @@
</section> </section>
<!-- Main Content --> <!-- Main Content -->
<div class="service-main"> <div class="container service-main">
<!-- Sidebar --> <!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('guide')}"></th:block> <th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('guide')}"></th:block>
<!-- Content --> <!-- Content -->
<section class="service-content"> <section class=" service-content">
<!-- Info Box --> <!-- Info Box -->
<div class="signup-info-box"> <div class="signup-info-box">
<div class="signup-info-box__icon"> <div class="signup-info-box__icon">
@@ -161,16 +160,18 @@
<!-- Step 5 --> <!-- Step 5 -->
<div class="signup-step"> <div class="signup-step">
<div class="signup-step__icon-box five"> <div class="signup-step__icon-box five">
<!-- <svg width="40" height="40" viewBox="0 0 32 32" fill="none"--> <!-- <svg width="40" height="40" viewBox="0 0 32 32" fill="none"-->
<!-- xmlns="http://www.w3.org/2000/svg">--> <!-- xmlns="http://www.w3.org/2000/svg">-->
<!-- <rect x="7" y="7" width="18" height="18" rx="2" stroke="#0049B4" stroke-width="1.6"--> <!-- <rect x="7" y="7" width="18" height="18" rx="2" stroke="#0049B4" stroke-width="1.6"-->
<!-- stroke-linecap="round" stroke-linejoin="round" />--> <!-- stroke-linecap="round" stroke-linejoin="round" />-->
<!-- <path d="M12 11H20" stroke="#0049B4" stroke-width="1.6" stroke-linecap="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"--> <!-- <path d="M12 15L14.5 17.5L20 12" stroke="#0049B4" stroke-width="1.6"-->
<!-- stroke-linecap="round" stroke-linejoin="round" />--> <!-- stroke-linecap="round" stroke-linejoin="round" />-->
<!-- </svg>--> <!-- </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"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
<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"/> 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> </svg>
</div> </div>
<div class="signup-step__card"> <div class="signup-step__card">
@@ -206,7 +207,7 @@
</div> </div>
<div class="signup-action"> <div class="signup-action">
<a th:href="@{/signup}" class="signup-action__btn">회원가입하러 가기</a> <a th:href="@{/signup}" class="btn-action-primary">회원가입하러 가기</a>
</div> </div>
</section> </section>
</div> </div>
@@ -1,196 +1,228 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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> <body>
<th:block layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<section class="oauth2-2legged"> <section class="oauth2-2legged">
<!-- Section 1: Hero --> <!-- Section 1: Hero -->
<header class="oauth2-2legged__hero"> <header class="oauth2-2legged__hero">
<div class="oauth2-2legged__hero-inner"> <div class="oauth2-2legged__hero-inner">
<div class="oauth2-2legged__hero-illust" aria-hidden="true"> <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"> <svg viewBox="0 0 280 200" xmlns="http://www.w3.org/2000/svg" role="img"
<defs> aria-label="Server-to-Server Token Flow">
<marker id="o2leg-hero-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto"> <defs>
<path d="M0,0 L10,5 L0,10 Z" fill="#0049b4"/> <marker id="o2leg-hero-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8"
</marker> markerHeight="8" orient="auto">
</defs> <path d="M0,0 L10,5 L0,10 Z" fill="#0049b4" />
<rect x="0" y="0" width="280" height="200" rx="20" fill="#FFFFFF"/> </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"/> <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> <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"/> <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="112" text-anchor="middle" font-size="10" font-weight="700"
<text x="200" y="126" text-anchor="middle" font-size="10" font-weight="700" fill="#0049b4">Open API</text> 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)"> <g transform="translate(60,30)">
<rect width="160" height="32" rx="6" fill="#1A1A2E"/> <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> <text x="14" y="21" font-family="'Fira Code', monospace" font-size="12"
</g> 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> <text x="140" y="170" text-anchor="middle" font-size="11" font-weight="600"
</svg> fill="#64748B">Server-to-Server Token Flow</text>
</div> </svg>
<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>
</div> </div>
<div class="oauth2-2legged__code-panel"> <div class="oauth2-2legged__hero-body">
<span class="oauth2-2legged__code-tag">cURL</span> <span class="oauth2-2legged__hero-eyebrow">
<pre class="oauth2-2legged__code-block"><span class="o2leg-cm"># 액세스 토큰 발급</span> <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> \ 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> \ -H <span class="o2leg-c">'Content-Type: application/x-www-form-urlencoded'</span> \
-d <span class="o2leg-c">'grant_type=client_credentials'</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> -d <span class="o2leg-c">'scope=read.accounts'</span>
<span class="o2leg-g"># 응답: 200 OK + JSON</span></pre> <span class="o2leg-g"># 응답: 200 OK + JSON</span></pre>
</div> </div>
</div> </div>
</section> </section>
<!-- Section 5: STEP 2 — 액세스 토큰 응답 --> <!-- Section 5: STEP 2 — 액세스 토큰 응답 -->
<section class="oauth2-2legged__step" aria-labelledby="o2leg-step2-title"> <section class="oauth2-2legged__step" aria-labelledby="o2leg-step2-title">
<span class="oauth2-2legged__eyebrow">STEP 2</span> <span class="oauth2-2legged__eyebrow">STEP 2</span>
<h2 class="oauth2-2legged__h2" id="o2leg-step2-title">액세스 토큰 응답</h2> <h2 class="oauth2-2legged__h2" id="o2leg-step2-title">액세스 토큰 응답</h2>
<p class="oauth2-2legged__desc">정상 발급 시 200 OK 와 함께 다음 JSON 을 반환합니다.</p> <p class="oauth2-2legged__desc">정상 발급 시 200 OK 와 함께 다음 JSON 을 반환합니다.</p>
<div class="oauth2-2legged__step-grid"> <div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__code-panel"> <div class="oauth2-2legged__code-panel">
<span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--ok">200 OK · JSON</span> <span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--ok">200 OK · JSON</span>
<pre class="oauth2-2legged__code-block">{ <pre class="oauth2-2legged__code-block">{
<span class="o2leg-c">"access_token"</span>: <span class="o2leg-y">"eyJhbGciOiJSUzI1NiJ9..."</span>, <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">"token_type"</span>: <span class="o2leg-y">"bearer"</span>,
<span class="o2leg-c">"expires_in"</span>: <span class="o2leg-p">86400</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> <span class="o2leg-g"># 응답 본문은 캐싱하여 expires_in 동안 재사용 가능</span></pre>
</div> </div>
<div class="oauth2-2legged__panel"> <div class="oauth2-2legged__panel">
<h3 class="oauth2-2legged__panel-title">응답 필드</h3> <h3 class="oauth2-2legged__panel-title">응답 필드</h3>
<table class="oauth2-2legged__table"> <table class="oauth2-2legged__table">
<thead> <thead>
<tr> <tr>
<th>FIELD</th> <th>FIELD</th>
<th>TYPE</th> <th>TYPE</th>
<th>설명</th> <th>설명</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td><code class="oauth2-2legged__field-name">access_token</code></td> <td><code class="oauth2-2legged__field-name">access_token</code></td>
<td>string</td> <td>string</td>
<td>Bearer 호출에 사용할 토큰</td> <td>Bearer 호출에 사용할 토큰</td>
</tr> </tr>
<tr> <tr>
<td><code class="oauth2-2legged__field-name">token_type</code></td> <td><code class="oauth2-2legged__field-name">token_type</code></td>
<td>string</td> <td>string</td>
<td>고정값 "bearer"</td> <td>고정값 "bearer"</td>
</tr> </tr>
<tr> <tr>
<td><code class="oauth2-2legged__field-name">expires_in</code></td> <td><code class="oauth2-2legged__field-name">expires_in</code></td>
<td>int</td> <td>int</td>
<td>유효 시간(초). 기본 86400</td> <td>유효 시간(초). 기본 86400</td>
</tr> </tr>
<tr> <tr>
<td><code class="oauth2-2legged__field-name">scope</code></td> <td><code class="oauth2-2legged__field-name">scope</code></td>
<td>string</td> <td>string</td>
<td>실제 부여된 권한 범위</td> <td>실제 부여된 권한 범위</td>
</tr> </tr>
<tr> <tr>
<td><code class="oauth2-2legged__field-name">jti</code></td> <td><code class="oauth2-2legged__field-name">jti</code></td>
<td>string</td> <td>string</td>
<td>토큰 고유 ID (감사/취소용)</td> <td>토큰 고유 ID (감사/취소용)</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </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> </div>
</section>
<h4 class="oauth2-2legged__panel-subtitle">토큰 재사용 가이드</h4> <!-- Section 6: STEP 3 — API 호출 -->
<ul class="oauth2-2legged__tips"> <section class="oauth2-2legged__step" aria-labelledby="o2leg-step3-title">
<li>동일 토큰은 expires_in(기본 86400초) 동안 재사용</li> <span class="oauth2-2legged__eyebrow">STEP 3</span>
<li>만료 임박 시 재발급 후 교체 (예: TTL의 80% 시점)</li> <h2 class="oauth2-2legged__h2" id="o2leg-step3-title">발급 토큰으로 API 호출</h2>
<li>매 호출마다 토큰을 새로 발급하지 마세요</li> <p class="oauth2-2legged__desc">Authorization 헤더에 Bearer 토큰을 실어 보호 자원 API 를 호출합니다.</p>
<li>권한이 다른 API 는 scope 별로 토큰을 분리 발급</li>
</ul>
</div>
<div class="oauth2-2legged__code-panel"> <div class="oauth2-2legged__step-grid">
<span class="oauth2-2legged__code-tag">cURL</span> <div class="oauth2-2legged__panel">
<pre class="oauth2-2legged__code-block"><span class="o2leg-cm"># 보호 자원 API 호출</span> <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> \ curl -X <span class="o2leg-y">GET</span> \
<span class="o2leg-c">'https://openapi.djbank.co.kr/api/v1/accounts'</span> \ <span class="o2leg-c">'https://openapi.djbank.co.kr/api/v1/accounts'</span> \
-H <span class="o2leg-c">'Authorization: Bearer eyJhbGciOiJSUzI..'</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> <span class="o2leg-g"># 응답</span>
HTTP/1.1 <span class="o2leg-g">200 OK</span> HTTP/1.1 <span class="o2leg-g">200 OK</span>
{ <span class="o2leg-c">"accounts"</span>: [ ... ] }</pre> { <span class="o2leg-c">"accounts"</span>: [ ... ] }</pre>
</div> </div>
</div> </div>
</section> </section>
<!-- Section 7: 대표 에러 응답 (표) --> <!-- Section 7: 대표 에러 응답 (표) -->
<section class="oauth2-2legged__errors" aria-labelledby="o2leg-errors-title"> <section class="oauth2-2legged__errors" aria-labelledby="o2leg-errors-title">
<span class="oauth2-2legged__eyebrow">ERROR CODES</span> <span class="oauth2-2legged__eyebrow">ERROR CODES</span>
<h2 class="oauth2-2legged__h2" id="o2leg-errors-title">대표 에러 응답</h2> <h2 class="oauth2-2legged__h2" id="o2leg-errors-title">대표 에러 응답</h2>
<div class="oauth2-2legged__panel"> <div class="oauth2-2legged__panel">
<table class="oauth2-2legged__table oauth2-2legged__table--errors"> <table class="oauth2-2legged__table oauth2-2legged__table--errors">
<thead> <thead>
<tr> <tr>
<th>HTTP</th> <th>HTTP</th>
<th>ERROR CODE</th> <th>ERROR CODE</th>
<th>의미</th> <th>의미</th>
<th>해결 가이드</th> <th>해결 가이드</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">400</span></td> <td><span
<td><code>invalid_request</code></td> class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">400</span>
<td>필수 파라미터 누락/형식 오류</td> </td>
<td>grant_type, client_id, client_secret 확인</td> <td><code>invalid_request</code></td>
</tr> <td>필수 파라미터 누락/형식 오류</td>
<tr> <td>grant_type, client_id, client_secret 확인</td>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span></td> </tr>
<td><code>invalid_client</code></td> <tr>
<td>자격증명 불일치 / 미승인</td> <td><span
<td>ClientID·Secret 재확인, 앱 상태 점검</td> class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span>
</tr> </td>
<tr> <td><code>invalid_client</code></td>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span></td> <td>자격증명 불일치 / 미승인</td>
<td><code>invalid_token</code></td> <td>ClientID·Secret 재확인, 앱 상태 점검</td>
<td>만료/위·변조된 토큰</td> </tr>
<td>토큰 재발급 후 재시도</td> <tr>
</tr> <td><span
<tr> class="oauth2-2legged__status-badge oauth2-2legged__status-badge--400">401</span>
<td><span class="oauth2-2legged__status-badge oauth2-2legged__status-badge--403">403</span></td> </td>
<td><code>insufficient_scope</code></td> <td><code>invalid_token</code></td>
<td>scope 권한 부족</td> <td>만료/위·변조된 토큰</td>
<td>필요 scope 추가 신청 후 재발급</td> <td>토큰 재발급 후 재시도</td>
</tr> </tr>
</tbody> <tr>
</table> <td><span
</div> class="oauth2-2legged__status-badge oauth2-2legged__status-badge--403">403</span>
</section> </td>
<td><code>insufficient_scope</code></td>
<td>scope 권한 부족</td>
<td>필요 scope 추가 신청 후 재발급</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- Section 8: 에러 응답 예시 (단일 사례) --> <!-- Section 8: 에러 응답 예시 (단일 사례) -->
<section class="oauth2-2legged__error-example" aria-labelledby="o2leg-err-ex-title"> <section class="oauth2-2legged__error-example" aria-labelledby="o2leg-err-ex-title">
<span class="oauth2-2legged__eyebrow">ERROR RESPONSE</span> <span class="oauth2-2legged__eyebrow">ERROR RESPONSE</span>
<h2 class="oauth2-2legged__h2" id="o2leg-err-ex-title">에러 응답 예시</h2> <h2 class="oauth2-2legged__h2" id="o2leg-err-ex-title">에러 응답 예시</h2>
<p class="oauth2-2legged__desc">자격증명 불일치 시 401 Unauthorized 와 함께 다음 JSON 을 반환합니다.</p> <p class="oauth2-2legged__desc">자격증명 불일치 시 401 Unauthorized 와 함께 다음 JSON 을 반환합니다.</p>
<div class="oauth2-2legged__step-grid"> <div class="oauth2-2legged__step-grid">
<div class="oauth2-2legged__code-panel oauth2-2legged__code-panel--error"> <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> <span class="oauth2-2legged__code-tag oauth2-2legged__code-tag--error">401 Unauthorized
<pre class="oauth2-2legged__code-block">{ · 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"</span>: <span class="o2leg-y">"invalid_client"</span>,
<span class="o2leg-c">"error_description"</span>: <span class="o2leg-c">"error_description"</span>:
<span class="o2leg-y">"Client authentication failed"</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> <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> </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>
</div> </section>
</section> </th:block>
</th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
</th:block> </th:block>
</html> </html>