TESOL 학생정보

CLASS-STUDENT-INFORMATION · /my-class/student-information

실제 화면 열기 ↗
기능
7
API 호출
0
가드레일
16
id 가 테스트 이름
구현 단계
32 / 32
완료수강 · 커리큘럼wvctesol-api :8080로그인 필요서버 컴포넌트16

개발 스펙

이 화면에서 할 수 있어야 하는 것 (2)

  • 내 정보 · 수강기간 · 결제 · 합격 · 자격증 발급 여부를 본다.
  • 조건이 되면 입학확인증과 스캔본을 받는다.

비즈니스 규칙 (3)

  • 입학확인증 행: 결제 + 준비완료라야 보인다.
    근거: studentInformation.php:87
  • 스캔본 행: 위 조건 + 사이버승인 + 합격이라야 보인다.
    근거: studentInformation.php:98
  • 합격일은 pass_date(YYYYMMDDHHmmss) 앞 8자리를 YYYY-MM-DD 로.
    근거: view_date($d, false)

API (6)

메서드경로권한모듈 · 설명
GET/api/learning/students/me/entrance수강생
입학확인증 내려받기. 파일 바이트를 그대로 준다.
domains:learning:learning-web
403 DOCUMENT_NOT_ALLOWED
404 DOCUMENT_NOT_FOUND
404 DOCUMENT_FILE_MISSING
GET/api/learning/students/me/scan수강생
스캔본 내려받기. 합격해야 받을 수 있다.
domains:learning:learning-web
403 DOCUMENT_NOT_ALLOWED
404 DOCUMENT_NOT_FOUND
GET/api/learning/students/me수강생
학생 정보와 자료 노출 조건.
domains:learning:learning-web
GET/api/learning/students/me/entrance수강생
입학확인증 다운로드.
domains:learning:learning-web
403 NOT_ELIGIBLE
GET/api/learning/students/me/certificate-address수강생
자격증 배송 주소 조회. 합격 + 자격증발급이라야 열린다.
domains:learning:learning-web
응답: { zip, address1, address2 }
403 CERTIFICATE_NOT_ISSUED
POST/api/learning/students/me/certificate-address수강생
배송 주소 저장. 대상은 언제나 토큰 주인이다.
domains:learning:learning-web
요청: { zip, address1, address2 }
403 CERTIFICATE_NOT_ISSUED
400 INVALID_ADDRESS

가드레일 (16) — id 가 곧 테스트 이름이다

id반드시 만족해야 하는 것등급
profile-change-requires-login
로그인하지 않으면 이름 · 영문 이름을 바꿀 수 없다.
critical
profile-english-name-saved
영문 이름을 바꾸면 저장되고 학생정보에 그대로 보인다.
high
profile-form-shown
이름과 영문 이름을 화면에서 고칠 수 있다.
원본은 값 옆에 [저장] 버튼을 두고 ajax 로 바꿨다(studentInformation.php:435~). 옮기는 동안 목업으로 두었던 것을 실제로 붙였다.
high
profile-form-saves
고친 값은 다시 열어도 남아 있다.
high
profile-form-shows-server-message
거절된 값은 서버가 준 문구를 그대로 보여 준다.
normal
document-path-stays-in-kind-dir
DB 의 파일 경로가 자료 종류 디렉터리를 벗어나면 읽지 않아야 한다.
루트만 검사하면 "/../scan/..." 으로 합격자 전용 스캔본을 입학확인증인 척 받아 갈 수 있다. 원본 _download_entrance.php 는 DB 값을 그대로 이어 붙였다.
critical
document-requires-login
토큰 없이 자료를 받을 수 없어야 한다.
critical
student-scan-requires-pass
합격 전에는 스캔본 다운로드가 노출되지도, API 로 받아지지도 않아야 한다.
critical
student-own-data-only
항상 로그인한 본인 정보만 나와야 한다.
critical
certificate-address-requires-issued
합격 + 자격증발급이 아니면 배송 주소를 조회도 저장도 할 수 없어야 한다.
원본 조건 그대로다(studentInformation.php:199). 화면에서 폼을 감추는 것만으로는 부족하다 — 직접 POST 하면 뚫린다.
critical
certificate-address-own-only
요청에 회원을 담아 보내도 남의 주소를 바꿀 수 없어야 한다.
원본은 폼의 아이디를 세션과 대조하고 어긋나면 실패 로그를 남겼다. 우리는 아예 받지 않는다 — 대상이 언제나 토큰의 memberId 라 지목할 방법이 없다.
critical
certificate-address-per-course
TESOL 과 TEC 주소가 섞이면 안 된다.
원본도 post_update_new.php · post_update_tec_new.php 로 나눴고 테이블에 course_code 가 있다.
high
certificate-address-validated
우편번호와 주소가 비었거나 형식이 틀리면 저장하지 않아야 한다.
컬럼이 varchar 라 길이를 넘기면 DB 에서 잘린다. 잘린 주소로는 배송이 안 된다.
high
certificate-address-hidden-without-issue
자격이 없으면 화면에 폼이 아예 나오지 않아야 한다.
normal
certificate-address-form-shape
주소 입력칸이 폼 안에 있어야 한다.
원본은 <table> 안에 <form> 을 넣었는데 그건 HTML 상 유효하지 않다. 브라우저가 폼을 표 밖으로 끌어내면 input 이 떨어져 나가 서버 액션이 아무것도 못 받는다.
normal
certificate-address-roundtrip
저장한 주소가 다시 읽혀야 하고, 두 번째 저장은 새 행이 아니라 갱신이어야 한다.
normal

구현 계획 (32/32)

  1. ProfileChange — 값 검사. 영문 이름은 영문자만, 30자까지identity-domain
  2. MemberProfileUseCase — 대상은 언제나 토큰의 회원identity-app
  3. PATCH /members/me/name · /members/me/english-nameidentity-web
  4. StudentInfo 에 englishName 추가learning-domain
  5. StudentDocument · DocumentStorage 포트 정의learning-domain
  6. LearningUseCase.document — 노출 조건 재검사 후 파일 반환learning-app
  7. LocalDocumentStorage — 업로드 루트에서 읽고 경로 조작 차단learning-infrastructure
  8. GET /students/me/entrance · /scan 구현learning-web
  9. 앱 라우트 /api/learning/entrance · /scan 연결wvctesol
  10. CurriculumUnit · ScoreReport · StudentInfo · 포트 2개learning-domain
  11. LearningUseCase · CourseCataloglearning-app
  12. Curriculum · Learner 어댑터learning-infrastructure
  13. /api/learning/students/me 구현learning-web
  14. /api/learning/students/me/entrance 구현learning-web
  15. Next.js 를 BFF 로 전환 — Prisma 직접 접근 제거wvctesol
  16. CertificateAddress · CertificateStatus 값 객체와 자격 판정learning-domain
  17. certificateStatus · certificateAddress · saveCertificateAddress 포트learning-domain
  18. 유스케이스 — 자격을 다시 보고 저장. 수강권은 보지 않는다learning-app
  19. certificate_post upsert 어댑터 (login_id + course_code 로 한 행)learning-infrastructure
  20. GET/POST /students/me/certificate-addresslearning-web
  21. 학생정보 화면의 주소 폼 — 다음 우편번호 조회 포함wvctesol
  22. StudentDocument · DocumentStorage 포트 정의learning-domain
  23. LearningUseCase.document — 노출 조건 재검사 후 파일 반환learning-app
  24. LocalDocumentStorage — 업로드 루트에서 읽고 경로 조작 차단learning-infrastructure
  25. GET /students/me/entrance · /scan 구현learning-web
  26. 앱 라우트 /api/learning/entrance · /scan 연결wvctesol
  27. CurriculumUnit · ScoreReport · StudentInfo · 포트 2개learning-domain
  28. LearningUseCase · CourseCataloglearning-app
  29. Curriculum · Learner 어댑터learning-infrastructure
  30. /api/learning/students/me 구현learning-web
  31. /api/learning/students/me/entrance 구현learning-web
  32. Next.js 를 BFF 로 전환 — Prisma 직접 접근 제거wvctesol

서버 사이드 구현 계획 — 자바 객체별 요구사항 (8/8)

객체모듈책임 · 지켜야 할 것
StudentDocument
record
learning-domain
수강생에게 내려 줄 파일 한 건.
  • Kind 열거형이 컬럼 이름을 정한다 — 바깥에서 온 문자열이 컬럼이 되지 않는다.
  • TESOL 은 entrance·scan, TEC 는 entrance_tec·scan_tec 으로 갈린다.
  • fileName() 은 경로의 마지막 조각이다. 원본도 그렇게 잘라 쓴다.
DocumentStorage
interface
learning-domain
파일 저장소 포트.
  • 바이트만 돌려준다 — 로컬이든 오브젝트 스토리지든 도메인은 알 필요가 없다.
  • 읽을 수 없으면 예외 대신 Optional.empty 다.
LocalDocumentStorage
class
learning-infrastructure
업로드 디렉터리에서 파일을 읽는다.
  • 경로 검사 기준은 업로드 루트가 아니라 **자료 종류별 디렉터리**다. 루트만 보면 스캔본 우회가 뚫린다.
  • DB 값 앞의 슬래시를 걷어낸다 — 그대로 resolve 하면 절대경로로 해석된다.
  • normalize 후 base.startsWith 로 확인하고, 벗어나면 로그를 남기고 거부한다.
  • 50MB 를 넘으면 읽지 않는다.
  • wvctesol.upload-root 가 비면 경고하고 항상 비어 있는 결과를 준다.
ScoreReport
record
learning-domain
성적표 계산. 원본 resultOfCourse.php:99~143.
  • 평균의 분모는 합격 유닛 수다. 전체 유닛 수로 나누면 안 된다.
  • 통과한 유닛에서 가장 나중 차수부터 거슬러 올라가 70점 이상인 첫 점수를 쓴다(C→B→A).
  • 합격 유닛이 0 이면 0 을 돌려준다 — 0 으로 나누지 않는다.
  • BigDecimal 로 소수점 2자리를 만든다. double 은 표시가 흔들린다.
CourseCatalog
class
learning-app
과정별 상수(유닛 수 · 차수 순서 · 시험 유무).
  • 원본이 _Env.php 와 _exam*.php 에 흩어 두었던 값을 한곳에 모은다.
  • PHONICS·TEACHING 은 hasExam=false — 이 과정에는 시험이 없다.
LearningUseCase
service
learning-app
커리큘럼 · 성적표 · 학생정보 조회.
  • 모든 조회가 수강권 확인을 먼저 한다. 과정마다 보는 플래그가 다르다.
  • memberId 는 토큰에서만 온다 — 파라미터로 받으면 남의 성적표를 볼 수 있다.
CurriculumRepositoryAdapter
class
learning-infrastructure
unit · lesson 조회.
  • unit · lesson 이 정본이다. 화면에 배열로 두면 DB 와 어긋난다(UNIT2 레슨 누락 버그의 원인).
  • left join 이라 레슨이 없는 유닛도 목록에 남는다.
LearnerRepositoryAdapter
class
learning-infrastructure
수강 자격 · 성적 · 학생정보 조회.
  • 과정별로 컬럼을 통째로 갈아 끼운다 — TESOL 은 pass_yn/certi_yn/entrance, TEC 은 *_tec_yn.
  • 스캔본 노출은 조건이 하나 더 붙는다: 결제+준비완료 **+ 사이버승인 + 합격**.
  • 합격일은 pass_date 앞 8자리를 YYYY-MM-DD 로 자른다.

테스트 방법 (17)

가드레일종류확인 방법
profile-change-requires-logine2e토큰 없이 PATCH → 401
profile-english-name-savede2e바꾼 뒤 학생정보 조회로 확인
profile-form-showne2e두 입력칸이 보인다
profile-form-savese2e저장 후 새로고침해도 값이 남는다
profile-form-shows-server-messagee2e한글을 넣으면 "영문자" 문구가 뜬다
certificate-address-requires-issuede2e합격하지 않은 계정으로 조회·저장 → 둘 다 403 CERTIFICATE_NOT_ISSUED
certificate-address-own-onlye2e요청 본문에 loginId·memberId 를 실어 보내도 남의 주소가 바뀌지 않는다
certificate-address-per-coursee2eTESOL 을 저장한 뒤 TEC 을 읽어 같은 주소가 아닌지
certificate-address-validatede2e빈 값 · 문자 우편번호 · 짧은 우편번호 → 400
certificate-address-hidden-without-issuee2e자격 없는 계정의 학생정보 화면에 폼이 0개
certificate-address-form-shapee2ezip · address1 · address2 가 form 의 자손인지
certificate-address-roundtripe2e저장 → 재조회 일치, 두 번째 저장도 갱신되는지
document-path-stays-in-kind-dirintegrationentrance 컬럼을 "/../scan/..." 으로 두고 요청 → 404, 로그에 차단 기록
document-requires-logine2e토큰 없이 /students/me/entrance · /scan 호출 → 401
student-scan-requires-passe2epass_yn=N 계정으로 /students/me/scan 호출 → 403 DOCUMENT_NOT_ALLOWED
student-scan-requires-passe2epass_yn=N 계정에서 스캔본 행이 없고, 다운로드 API 직접 호출 시 403
student-own-data-onlye2e토큰의 회원과 다른 회원 정보가 응답에 섞이지 않음

스냅샷

로그인 세션으로 촬영한 실제 화면입니다.

CLASS-STUDENT-INFORMATION 스냅샷

기능 목록 (7)

입력
  • ·3개
  • ·제출 버튼2개
  • ·입력 필드4개
상태
  • ·화면 상태value, zip, address1
인증
  • ·세션 확인
  • ·로그인 필요proxy 가 /login 으로 보냄
하드코딩
  • ·하드코딩 데이터tabs

화면 이동

← 이 화면으로 오는 곳

없음

이 화면에서 가는 곳 →

연결

API

직접 호출하는 API 가 없습니다

소스
src/app/my-class/student-information/page.tsx