-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
프로젝트 세팅 및 회원가입 기능 개발 #2
Conversation
- base model - Instructor / Student 구조 개발
- Base Model 삭제 - Base Model 상속 받는 학생 / 교수자 모델, dto 전체 수정 - application.yml 오타 수정 (mybatis 부분)
- 홈페이지 / 회원가입 페이지 / 로그인 페이지 / 대시보드 페이지
- 로그인 기능 구현 (JWT 토큰 발급 및 검증) - 학생 / 교수자 로그인 API 개발 - 로그인 시 승인 상태 확인 (교수자는 관리자 승인 필요, 학생은 이메일 인증 필요) - 대시보드 페이지 기본 구조 구현 (학생 / 교수자 대시보드 분리) - Spring Security 설정 수정: - 기본 로그인 폼 비활성화 (`formLogin().disable()`) - JWT 기반 인증 적용(AccessToken) - 특정 경로 접근 허용 (`permitAll()`) 설정 조정 - MyBatis 연동 및 인증 관련 SQL 수정 (`activate` 컬럼 관련 오류 해결) - 로그인 비밀번호 암호화 (`BCryptPasswordEncoder`) 확인 및 검증 로직 추가 - 오류 로그 확인 및 디버깅 (403 Forbidden, Invalid Password 등)
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
@MapperScan("com.gatheria.mapper") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오잉 이게 필요한가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mybatis 오류가 있었어서 이것저것 해보다가 시도했던 건데 지우는걸 까먹었네요 수정해두도록 하겠습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아직 안지워진거같아요!
.requestMatchers( | ||
"/", "/index.html", "/static/**", "/assets/**", | ||
"/login", "/register", "/api/auth/**", "/api/member/email-check" , "/api/member/student/register", "/api/member/instructor/register" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"/", "/index.html", "/static/", "/assets/"
요거는 없어도 될거같아요.
사실 메인 페이지는 프론트가 제작하는 것이지, 백엔드에서 관여하는건 아니라서요! (서버사이드렌더링이 아닌이상)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 하던게 서버사이드 렌더링이었나 봅니다. 지웠더니 렌더링이 안됩니다. 나머지 문제 해결 후에 수정해보겠습니다...!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정 어려우면 일단 넘어가시죠..!
public void addViewControllers(ViewControllerRegistry registry) { | ||
registry.addViewController("/register").setViewName("forward:/index.html"); | ||
registry.addViewController("/").setViewName("forward:/index.html"); | ||
registry.addViewController("/admin").setViewName("forward:/index.html"); | ||
registry.addViewController("/login").setViewName("forward:/index.html"); | ||
registry.addViewController("/dashboard").setViewName("forward:/index.html"); | ||
registry.addViewController("/dashboard/instructor").setViewName("forward:/index.html"); | ||
registry.addViewController("/dashboard/student").setViewName("forward:/index.html"); | ||
} | ||
|
||
@Override | ||
public void addResourceHandlers(ResourceHandlerRegistry registry) { | ||
registry.addResourceHandler("/**") | ||
.addResourceLocations("classpath:/static/"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요것도 필요없지 않을까요~?? ai 쓰시는거면 아예 별도 리액트 레포를 파셔서 연결해보시면 좋을듯싶어요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요 문제도 프론트 문제 정리하고 수정 반영하겠습니다...!
.setIssuedAt(new Date()) | ||
.setExpiration(new Date(System.currentTimeMillis() + expirationTime)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issuedAt 시각에서 expirationTime을 더해야 하지 않을까요?
지금은 issuedAt할 때 따로 현재 시각 구하고, expiration할때 현재 시각 구하는데 이러면 현재 시각이 약간 달라질 수 있어요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 반영안된거같아요..!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Date now = new Date();
Date expiration = new Date(now.getTime() + expirationTime);
...
setIssuedAt(now)
.setExpiration(expiration)
한번 생성된 now를 활용하여 setIssuedAt과 setExpiration을 설정하도록 변경했습니다.
public boolean validateAccessToken(String token) { | ||
//TODO : Access Token 검증 로직 | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어떻게 검증할까요 ㅎㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
답변 부탁드립니다..!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try {
Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token);
Claims claims = extractClaims(token);
Date expiration = claims.getExpiration();
return !expiration.before(new Date());
} catch (JwtException e){
System.out.println("JWT 검증 실패: " + e.getMessage());
return false;
}
토큰의 서명을 검증한 후, Claims에서 만료 시간을 추출하여 현재 시각과 비교하는 방식으로 유효성을 검사하도록 구현했습니다|
(다른 브랜치에서 추가 개발한 내용이라 PR에 코드가 없어 코멘트로 남깁니다)
import org.slf4j.LoggerFactory; | ||
|
||
@Getter | ||
public class BaseMember { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 abstract class Member를 선언하고 하위 클래스로 Student, Instructor로 설계했으면 합니다.
테이블의 경우 총 3개가 나올 것이고 학생과 교수는 member_id를 외래 키로 가지고 있으면 됩니다.
기존 방식은 중복 컬럼을 불필요하게 들고 있어야 하고, 컬럼 변경이 생기면 양쪽을 바꿔야 한다는 문제점이 있습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 abstract class Member를 사용하여 Student와 Instructor를 상속하는 구조로 변경했습니다.
이후 API 설계 시, 역할별 엔드포인트(/student/{id}, /instructor/{id})와 통합 엔드포인트(/member/{id}) 중 어떤 방식이 더 적절할까요?
예시
과제 생성 기능은 교수자만 사용 가능해야 하지만, 과제 상태 조회는 교수자 학생 모두 접근가능해야함
- 교수자는 모든 학생의 제출 상태를 조회
- 학생은 본인의 제출 상태만 조회 가능
요구사항을 고려했을 때, 어떤 규칙을 정해두고 설계를 하는게 적절할까요?
if ("student".equalsIgnoreCase(role)) { | ||
return authenticateStudent(request); | ||
} else if ("instructor".equalsIgnoreCase(role)) { | ||
return authenticateInstructor(request); | ||
} else { | ||
throw new RuntimeException("Invalid role"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
student, instructor은 하드 코딩 하지 말고 "role" 자체를 enum으로 뺄 수 없을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MemberRole 작성해서 반영했습니다
throw new RuntimeException("Invalid Password"); | ||
} | ||
|
||
String accessToken = jwtUtil.createAccessToken(request.getEmail(),"student"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분도 "student" 하드 코딩을 피했으면 합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MemberRole enum 클래스 사용해서 사용하도록 코드 수정했습니다.
<insert id="insertInstructor" useGeneratedKeys="true" keyProperty="id"> | ||
INSERT INTO instructors (email, password, name, phone, affiliation, active, created_at, updated_at) | ||
VALUES (#{email}, #{password}, #{name}, #{phone}, #{affiliation}, #{active}, NOW(), NOW()) | ||
</insert> | ||
|
||
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="id" > | ||
INSERT INTO students (email, password, name, phone, active, created_at, updated_at) | ||
VALUES (#{email}, #{password}, #{name}, #{phone}, #{active}, NOW(), NOW()) | ||
</insert> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
xml보다 mapper 단에서 어노테이션으로 쿼리 적는 것에 대해서는 어떻게 생각하시나요~?? 요거는 취향 차이긴해서 수정하라는 의미는 아닙니당 (의견을 여쭙는 정도)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 방법을 같이쓰는 것도 괜찮나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네
간단한건 쿼리 베이스로 가고, 복잡한건 xml로 많이 씁니당
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
간단한건 어노테이션으로 변경해서 수정해보겠습니다~
- Member 상속 구조 도입 - DB 테이블 구조 개선 (members + students/instructors) - MemberRole enum으로 설정 후 하드코딩 방지 - 불필요한 role 구분 제거 (이메일 중복 체크)
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
@MapperScan("com.gatheria.mapper") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아직 안지워진거같아요!
.setIssuedAt(new Date()) | ||
.setExpiration(new Date(System.currentTimeMillis() + expirationTime)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 반영안된거같아요..!
public boolean validateAccessToken(String token) { | ||
//TODO : Access Token 검증 로직 | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
답변 부탁드립니다..!
if (token == null || !token.startsWith("Bearer ")) { | ||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("토큰이 필요합니다."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
답변 부탁드립니다..!
public ResponseEntity<?> registerUser(@RequestBody InstructorRegisterRequestDto request) { | ||
InstructorRegisterResponseDto response = memberService.register(request); | ||
return ResponseEntity.ok(response); | ||
} | ||
|
||
|
||
@PostMapping("/student/register") | ||
public ResponseEntity<?> registerUser(@RequestBody StudentRegisterRequestDto request) { | ||
StudentRegisterResponseDto response = memberService.register(request); | ||
return ResponseEntity.ok(response); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기도 ResponseEntity<?>가 아닌 명확한 표현 부탁드립니다.
사실 ? = Object랑 다를바가 없어서 제네릭의 이점을 누리지 못합니다 ㅠ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 전체적으로 와일드카드로 된 부분 수정했습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 무슨 파일인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요것도 제거 완료했습니다
src/main/resources/static/vite.svg
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제거해주시죵 ㅋㅋㅋ 로고 같네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요것도 제거했습니다
src/main/resources/static/index.html
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거도 필요없지 않은지..?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요것도 제거했습니다
<insert id="insertInstructor" useGeneratedKeys="true" keyProperty="id"> | ||
INSERT INTO instructors (email, password, name, phone, affiliation, active, created_at, updated_at) | ||
VALUES (#{email}, #{password}, #{name}, #{phone}, #{affiliation}, #{active}, NOW(), NOW()) | ||
</insert> | ||
|
||
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="id" > | ||
INSERT INTO students (email, password, name, phone, active, created_at, updated_at) | ||
VALUES (#{email}, #{password}, #{name}, #{phone}, #{active}, NOW(), NOW()) | ||
</insert> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네
간단한건 쿼리 베이스로 가고, 복잡한건 xml로 많이 씁니당
.requestMatchers( | ||
"/", "/index.html", "/static/**", "/assets/**", | ||
"/login", "/register", "/api/auth/**", "/api/member/email-check" , "/api/member/student/register", "/api/member/instructor/register" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정 어려우면 일단 넘어가시죠..!
|
구현 내용
메인 리뷰어 지정 및 Due Date
@blue000927
고민 포인트, 리뷰 시 참고 사항(코드)
코드 바로가기
BaseMember 클래스를 만들어 학생(Student)과 교수(Instructor)의 공통 속성을 관리하려 했으나
BaseMember 없이 개별 테이블(Student, Instructor)로 변경하는 방향으로 수정하였습니다.
기존 방식 (BaseMember 사용)
• BaseMember 엔티티를 생성하여 email, password, name, phone, isActive 등의 공통 속성을 관리
• Student, Instructor가 BaseMember를 참조하는 형태로 구성
• DB 테이블은 base_members, students, instructors로 분리
현재 방식 (BaseMember 제거)
• Student와 Instructor 테이블을 별도로 두고 각 테이블에 공통 필드를 개별적으로 포함
• DB 테이블은 students, instructors 두 개로 구성됨
고민 : 다른 접근 방식이 있을지?
관련 이슈 : #1 회원가입
체크리스트