김남준(팀장) |
석지명 |
|
🙋 프론트엔드 리드 - CSS 마크업 구현 - 리뷰, 페이지 마크업 구현 - 페이지별 API 통신 로직 개발 - 개발자 경험 개선을 위한 코드 리팩토링 |
🙋 백엔드 리드 - DB설계 및 SQL 작성 - API를 활용한 데이터 크롤링 - Spring Boot를 이용한 REST API 구현 - 리뷰 수정, 삭제 기능 개발 - Jwt, 네비게이션 가드를 이용한 로그인 기능 개발 |
🟢 웹 아키텍처를 이해하고 이를 활용하여 웹 프로젝트를 설계하고 구현
🟢 SpringBoot와 MyBatis연동을 활용한 RestAPI 서버 구현
🟢 E-R Diagram
🟢 주어진 ERD를 통해 MySQL로 DB 및 테이블을 작성한다.
CREATE SCHEMA allclimbing;
USE allclimbing;
DROP TABLE IF EXISTS `user`;
DROP TABLE IF EXISTS `gym`;
DROP TABLE IF EXISTS `review`;
DROP TABLE IF EXISTS `likedgym`;
CREATE TABLE IF NOT EXISTS `user`(
`userId` varchar(45) CHARACTER SET utf8mb4 NOT NULL,
`password` varchar(45) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
`email` varchar(100) CHARACTER SET utf8mb4 NOT NULL UNIQUE DEFAULT '',
`nickname` varchar(45) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
`instagram` varchar(45) CHARACTER SET utf8mb4 DEFAULT '',
`grade` int(12) NOT NULL DEFAULT '0',
PRIMARY KEY (`userId`)
);
CREATE TABLE IF NOT EXISTS `gym`(
`gymId` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`gymAddress` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`gymName` varchar(60) CHARACTER SET utf8mb4 NOT NULL,
`gymImgURL` varchar(2000) CHARACTER SET utf8mb4,
`operationTime` varchar(45) CHARACTER SET utf8mb4,
`area` int(30) NOT NULL,
`contact` varchar(60) CHARACTER SET utf8mb4,
`shower` boolean NOT NULL,
`parking` boolean NOT NULL,
`tutoring` boolean NOT NULL,
`homepage` varchar(45) CHARACTER SET utf8mb4,
`detail` varchar(2000) CHARACTER SET utf8mb4,
PRIMARY KEY (`gymId`)
);
CREATE TABLE IF NOT EXISTS `review`(
`reviewNo` int(52) AUTO_INCREMENT NOT NULL,
`gymId` varchar(45) CHARACTER SET utf8mb4 NOT NULL,
`userId` varchar(45) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
`reviewImgURL` varchar(500) CHARACTER SET utf8mb4,
`originalFileName` varchar(500) CHARACTER SET utf8mb4,
`saveFileName` varchar(500) CHARACTER SET utf8mb4,
`content` varchar(200) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
`visitDate` DATE NOT NULL,
`regDate` TIMESTAMP DEFAULT now() ,
PRIMARY KEY (`reviewNo`),
CONSTRAINT `gym_ibfk_1` FOREIGN KEY (`gymId`) REFERENCES `gym` (`gymId`),
CONSTRAINT `user_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`userId`)
);
CREATE TABLE IF NOT EXISTS `likedgym`(
`userId` varchar(45) CHARACTER SET utf8mb4 NOT NULL,
`gymId` varchar(45) CHARACTER SET utf8mb4 NOT NULL,
PRIMARY KEY (`userId`, `gymId`),
FOREIGN KEY (`userId`) REFERENCES `user` (`userId`),
FOREIGN KEY (`gymId`) REFERENCES `gym` (`gymId`)
);
🟢 카카오맵 API를 활용하여 클라이밍 암장 데이터들을 모두 크롤링하여 DB에 집어넣는다. 유저 및 리뷰, 찜 데이터는 더미데이터로 넣어놓는다.
🔹 클라이밍 암장 테이블(카카오맵 API를 이용한 크롤링)
🔹 리뷰 테이블
🔹 유저 테이블 / 자주가는암장 중계 테이블
🟢 Spring Starter Project로 프로젝트를 시작한다. - Type : maven - Java Version : 8 - Spring Boot Version : 2.7.17 - dependencies : Spring Boot DevTools, Spring Web Services, MyBatis Framework, MySQL Driver, SpringFox Swagger UI(springfox-boot-starter), Java JWT
🟢 applicaition.properties, Configuration을 작성하여 Spring Boot를 사용하고 DB와 연결하기 위한 설정을 진행한다.
🔹 applicaition.properties <br>
🔹 DBconfig <br>
🔹 WebConfig & SwaggerConfig
# dataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/allclimbing?serverTimezone=UTC
spring.datasource.username=ssafy
spring.datasource.password=ssafy
# mybatis
mybatis.mapper-locations=classpath*:mappers/*.xml
mybatis.type-aliases-package=com.ssafy.climbing.model.dto//DBconfig
@Configuration
@MapperScan(basePackages = "com.ssafy.climbing.model.dao")
public class DBConfig {
}//WebConfig
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
}
// SwaggerConfig
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ssafy.climbing.model.controller"))
.paths(PathSelectors.ant("/api*/**"))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SSAFY_최종PJT_올클라이밍")
.description("7반 김남준 & 석지명")
.version("0.1")
.build();
}
}🟢 DTO(Gym, Review, User 클래스)를 작성한다. (Getter, Setter 파트 생략)
//Gym DTO
public class Gym {
private String gymId;
private String gymImgURL;
private String gymName;
private String gymAddress;
private String operationTime;
private int area;
private String contact;
private boolean shower;
private boolean parking;
private boolean tutoring;
private String homepage;
private String detail;
}
//Review DTO
public class Review {
private int reviewNo;
private String gymId;
private String userId;
private String reviewImgURL;
private String originalFileName;
private String saveFileName;
private String content;
private String visitDate;
private String regDate;
}
//User DTO
public class User {
private String userId;
private String password;
private String email;
private String nickname;
private String instagram;
}🟢 DAO(GymDao, ReviewDao, UserDao 클래스)를 작성한다.
//GymDao
public interface GymDao {
//암장 목록 조회
List<Gym> selectAllGym();
//암장 1개 조회
Gym selectOneGym(String gymId);
//한 유저가 자주가는 암장 목록 조회
List<Gym> selectFavoriteGym(String userId);
//암장 데이터베이스 삽입
int insertGym(Gym gym);
//암장 정보 업데이트
int updateGym(Gym gym);
//암장 찜 체크용
Keyword checkFavoriteGym(@Param("userId") String userId, @Param("gymId") String gymId);
//암장 찜 기능
int addFavoriteGym(Keyword keyword);
//암장 찜 해제 기능
int deleteFavoriteGym(Keyword keyword);
}
//ReviewDao
public interface ReviewDao {
// 리뷰 등록
int insertReview(Review review);
// 해당 암장의 리뷰들 조회
List<Review> selectGymReview(String gymId);
// 유저가 쓴 리뷰들 모두 조회
List<Review> selectUserReview(String userId);
// 리뷰 한개 조회
Review selectOneReview(@Param("reviewNo") int reviewNo);
// 리뷰 수정
int updateReview(Review review);
// 리뷰 삭제
int deleteReview(@Param("reviewNo") int reviewNo);
}
//UserDao
public interface UserDao {
// 유저 생성
int insertUser(User user);
// 유저 수정
int updateUser(User user);
// 유저 삭제
int deleteUser(String userId);
// 유저 조회
User selectOneUser(String userId);
// 유저 목록 조회
List<User> selectAllUser();
}🟢 마지막으로 Mappers(GymMapper, ReviewMapper, UserMapper)을 작성하여 DB와 서버의 연동을 완료한다.
<!-- GymMapper.xml -->
<mapper namespace="com.ssafy.climbing.model.dao.GymDao">
<!-- 암장 목록 조회 -->
<select id = "selectAllGym" resultType = "Gym">
SELECT *
FROM gym
</select>
<!-- 암장 1개 조회 -->
<select id ="selectOneGym" resultType = "Gym" parameterType = "String">
SELECT *
FROM gym
WHERE gymId = #{gymId}
</select>
<!-- 자주가는 암장목록 조회 -->
<select id="selectFavoriteGym" resultType="Gym" parameterType="String">
SELECT v.gymId, v.gymAddress, v.gymName, v.gymImgURL, v.operationTime, v.area, v.contact, v.shower, v.parking, v.tutoring, v.homepage, v.detail FROM
gym v JOIN likedgym u ON v.gymId = u.gymId
WHERE u.userId = #{userId}
</select>
<!-- 암장 DB에 추가 -->
<insert id="insertGym" parameterType="Gym">
INSERT IGNORE INTO `gym` (gymId, gymName, gymAddress, operationTime, area, contact, shower, parking, tutoring, homepage, detail)
VALUES
(#{gymId}, #{gymName}, #{gymAddress}, #{operationTime}, #{area}, #{contact}, #{shower}, #{parking}, #{tutoring}, #{homepage}, #{detail});
</insert>
<!-- 암장 정보 업데이트 -->
<update id="updateGym" parameterType="Gym">
UPDATE `gym`
SET gymName = #{gymName},gymAddress = #{gymAddress}
,operationTime = #{operationTime},area = #{area}
,contact = #{contact},shower = #{shower}, parking = #{parking}
,tutoring = #{tutoring},homepage = #{homepage}, detail = #{detail}
WHERE gymId = #{gymId}
</update>
<!-- 암장 찜 체크 -->
<select id="checkFavoriteGym" resultType="Keyword">
SELECT * FROM `likedgym`
WHERE gymId = #{gymId} AND userId = #{userId}
</select>
<!-- 암장 찜 기능 -->
<insert id="addFavoriteGym" parameterType="Keyword">
INSERT INTO `likedgym` (userId, gymId)
VALUES
(#{userId}, #{gymId});
</insert>
<!-- 암장 찜 해제 기능 -->
<delete id="deleteFavoriteGym" parameterType="Keyword">
DELETE FROM `likedgym`
WHERE gymId = #{gymId} AND userId = #{userId}
</delete>
</mapper>
<!-- ReviewMapper.xml -->
<mapper namespace="com.ssafy.climbing.model.dao.ReviewDao">
<insert id="insertReview" parameterType="Review">
INSERT INTO review (gymId, userId, reviewImgURL, originalFileName, saveFileName, content, visitDate, regDate)
VALUES
(#{gymId}, #{userId}, #{reviewImgURL}, #{originalFileName}, #{saveFileName}, #{content}, #{visitDate}, NOW());
</insert>
<select id="selectGymReview" resultType="Review" parameterType="String">
SELECT *
FROM review
WHERE gymId = #{gymId};
</select>
<select id="selectUserReview" resultType="Review" parameterType="String">
SELECT *
FROM review
WHERE userId = #{userId};
</select>
<select id="selectOneReview" resultType="Review">
SELECT *
FROM review
WHERE reviewNo = #{reviewNo};
</select>
<update id="updateReview" parameterType="Review">
UPDATE review
SET content = #{content}
WHERE reviewNo = #{reviewNo};
</update>
<delete id="deleteReview">
DELETE FROM review
WHERE reviewNo = #{reviewNo};
</delete>
</mapper>
<!-- UserMapper.xml -->
<mapper namespace="com.ssafy.climbing.model.dao.UserDao">
<!-- 유저 생성 -->
<insert id="insertUser" parameterType="User">
INSERT INTO user (userId, password, email, nickname, instagram)
VALUES (#{userId}, #{password}, #{email}, #{nickname}, #{instagram});
WHERE NOT EXISTS (SELECT * FROM user WHERE userId = #{userId});
</insert>
<!-- 유저 수정 -->
<update id="updateUser" parameterType = "User">
UPDATE user
SET password = #{password} AND email = #{email} AND nickname = #{nickname} AND instagram = #{instagram}
WHERE userId = #{userId};
</update>
<!-- 유저 삭제 -->
<delete id="deleteUser">
DELETE FROM user
WHERE userId = #{userId};
</delete>
<!-- 유저 조회 -->
<select id="selectOneUser" resultType="User" parameterType="String">
SELECT *
FROM user
WHERE userId = #{userId};
</select>
<!-- 유저 목록 조회 -->
<select id="selectAllUser" resultType="User">
SELECT *
FROM user;
</select>
</mapper>
🟢 Service(GymService, ReviewService, UserService 클래스)를 작성한다. - 코드 생략
🟢 Controller(GymController, ReviewController)를 작성하여 핵심 기능을 우선적으로 구현한다.
@RestController, @Get/Post/Put/Delete+Mapping @RequestBody 등 Annotation을 이용하여 RESTAPI 서버를 구현한다.
UserController는 핵심기능 API 테스트 후 Jwt토큰을 이용하여 프론트엔드와 협업하여 작성할 예정
//GymController
@RestController
@RequestMapping("/api/gym")
@Api(tags="암장 컨트롤러")
@CrossOrigin("*")
public class GymController {
@Autowired
private GymService gService;
@Autowired
private UserService uService;
@Autowired
private ReviewService rService;
@GetMapping("/list")
@ApiOperation(value="암장 목록 조회")
public ResponseEntity<?> allGymList(){
List<Gym> gymList = gService.selectAllGym();
return new ResponseEntity<List<Gym>>(gymList, HttpStatus.OK);
}
@GetMapping("/list/{userId}")
@ApiOperation(value="자주가는 암장 목록 조회")
public ResponseEntity<?> favoriteGymList(@PathVariable String userId){
List<Gym> gymList = gService.selectFavoriteGym(userId);
return new ResponseEntity<List<Gym>>(gymList, HttpStatus.OK);
}
@GetMapping("/{gymId}")
@ApiOperation(value="암장 상세조회")
public ResponseEntity<?> selectGym(@PathVariable String gymId){
Gym gym = gService.selectOneGym(gymId);
return new ResponseEntity<Gym>(gym, HttpStatus.OK);
}
@PostMapping("/insert")
@ApiOperation(value="암장 정보삽입")
public ResponseEntity<?> insertGym(@RequestBody Gym gym){
int result = gService.insertGym(gym);
if (result == 0) {
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
}
@PutMapping("/update")
@ApiOperation(value="암장 정보업데이트")
public ResponseEntity<?> updateGym(@RequestBody Gym gym){
System.out.println(gym.toString());
int result = gService.updateGym(gym);
if (result == 0) {
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
}
@PostMapping("/favorite")
@ApiOperation(value="암장 찜 체크용")
public ResponseEntity<?> checkFavoriteGym(@RequestBody Keyword keyword){
Keyword resultType = gService.checkFavoriteGym(keyword.getUserId(), keyword.getGymId());
if (resultType == null) {
return new ResponseEntity<Boolean>(false, HttpStatus.OK);
}
System.out.println(resultType.toString());
return new ResponseEntity<Boolean>(true, HttpStatus.ACCEPTED);
}
@PostMapping("/favorite/{gymId}")
@ApiOperation(value="암장 찜")
public ResponseEntity<?> addFavoriteGym(@RequestBody Keyword keyword){
int result = gService.addFavoriteGym(keyword);
if (result == 0) {
return new ResponseEntity<Integer>(result, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Integer>(result, HttpStatus.ACCEPTED);
}
@PostMapping("/favorite/delete")
@ApiOperation(value="암장 찜 해제")
public ResponseEntity<?> deleteFavoriteGym(@RequestBody Keyword keyword){
System.out.println("찜해제?"+keyword.toString());
int result = gService.deleteFavoriteGym(keyword);
if (result == 0) {
return new ResponseEntity<Integer>(result, HttpStatus.NOT_ACCEPTABLE);
}
return new ResponseEntity<Integer>(result, HttpStatus.ACCEPTED);
}
}
//ReviewController
@RestController
@RequestMapping("/api/review")
@Api(tags="리뷰 컨트롤러")
@CrossOrigin("*")
public class ReviewController {
@Autowired
private GymService vService;
@Autowired
private UserService uService;
@Autowired
private ReviewService rService;
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//=======================리뷰파트 CURD 구현===========================
@GetMapping("/{gymId}")
@ApiOperation(value="암장 리뷰 조회")
public ResponseEntity<List<Review>> gymReviewlist(@PathVariable String gymId){
List<Review> reviewList = rService.getGymReview(gymId);
return new ResponseEntity<List<Review>>(reviewList, HttpStatus.OK);
}
@GetMapping("/list/{userId}")
@ApiOperation(value="유저 리뷰 조회")
public ResponseEntity<List<Review>> userReviewlist(@PathVariable String userId){
List<Review> reviewList = rService.getUserReview(userId);
return new ResponseEntity<List<Review>>(reviewList, HttpStatus.OK);
}
@GetMapping("/select/{reviewNo}")
@ApiOperation(value="리뷰 한개 조회")
public ResponseEntity<?> selectOneReview(@PathVariable int reviewNo){
Review review = rService.getOneReview(reviewNo);
if (review == null) {
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Review>(review, HttpStatus.OK);
}
@PostMapping("/write")
@ApiOperation(value="리뷰 작성")
public ResponseEntity<Void> write(@RequestPart(value="image", required = false) MultipartFile file, @RequestPart("review") Review review){
//파일이 존재하고 가짜파일이 아닐때만
if (file != null && file.getSize() > 0) {
String uploadPath = "C:\\uploadTemp";
File folder = new File(uploadPath);
if (!folder.exists()) {
folder.mkdir();
}
//작성일까지만 불러온다
String writtenDay = sdf.format(System.currentTimeMillis());
//작성일 폴더가 없다면 폴더 생성
//저장될 폴더경로
String saveFolder = uploadPath+"/"+writtenDay;
folder = new File(saveFolder);
if (!folder.exists()) {
folder.mkdir();
}
//실제 파일이름을 가져와
//기존 파일이름
String originalFileName = file.getOriginalFilename();
int nameLength = originalFileName.length();
//저장될 파일이름
UUID uuid = UUID.randomUUID();
String saveFileName = uuid.toString()+originalFileName.substring(nameLength-4, nameLength);
// 파일 Dto 생성하여 우선 경로만
// DAO를 통해 저장
review.setReviewImgURL(saveFolder);
review.setOriginalFileName(originalFileName);
review.setSaveFileName(saveFileName);
File target = new File(saveFolder, saveFileName);
//target에 file복사
try {
FileCopyUtils.copy(file.getBytes(), target);
} catch (IOException e) {
System.out.println("왜 예외로들어오는거지");
e.printStackTrace();
}
}
int result = rService.writeReview(review);
if (result == 0) {
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
}
@PutMapping("/update")
@ApiOperation(value="리뷰 수정")
public ResponseEntity<Void> update(@RequestPart(value="image", required = false) MultipartFile file, @RequestPart("review") Review review){
if (file != null && file.getSize() > 0) {
String uploadPath = "C:\\uploadTemp";
File folder = new File(uploadPath);
if (!folder.exists()) {
folder.mkdir();
}
//작성일까지만 불러온다
String writtenDay = sdf.format(System.currentTimeMillis());
System.out.println(writtenDay);
//작성일 폴더가 없다면 폴더 생성
//저장될 폴더경로
String saveFolder = uploadPath+"/"+writtenDay;
folder = new File(saveFolder);
if (!folder.exists()) {
folder.mkdir();
}
//실제 파일이름을 가져와
//기존 파일이름
String originalFileName = file.getOriginalFilename();
int nameLength = originalFileName.length();
//저장될 파일이름
UUID uuid = UUID.randomUUID();
String saveFileName = uuid.toString()+originalFileName.substring(nameLength-4, nameLength);
// 파일 Dto 생성하여 우선 경로만
// DAO를 통해 저장
review.setReviewImgURL(saveFolder);
review.setOriginalFileName(originalFileName);
review.setSaveFileName(saveFileName);
File target = new File(saveFolder, saveFileName);
//target에 file복사
try {
FileCopyUtils.copy(file.getBytes(), target);
} catch (IOException e) {
System.out.println("왜 예외로들어오는거지");
e.printStackTrace();
}
}
int result = rService.modifyReview(review);
if (result == 0) {
return new ResponseEntity<Void>(HttpStatus.NOT_MODIFIED);
}
return new ResponseEntity<Void>(HttpStatus.OK);
}
@DeleteMapping("/delete/{reviewNo}")
@ApiOperation(value="리뷰 삭제")
public ResponseEntity<Void> delete(@PathVariable int reviewNo){
int result = rService.removeReview(reviewNo);
if (result == 0) {
return new ResponseEntity<Void>(HttpStatus.NOT_ACCEPTABLE);
}
return new ResponseEntity<Void>(HttpStatus.OK);
}
}🟢 토큰 생성과 유효성 검사를 위한 JwtUtil 클래스를 작성한다.
@Component
public class JwtUtil {
private static final String SALT = "SSAFIT";
//토큰 생성 메서드
//데이터가 하나만 인자로 들어온다라고 가정하고 / 테스트에 방해가 되는 유효기간 제거
public String createToken(String key, String value) throws UnsupportedEncodingException {
return Jwts.builder()
.setHeaderParam("alg", "HS256")
.setHeaderParam("typ", "JWT") //헤더완료
.claim(key, value)
.setExpiration(new Date(System.currentTimeMillis()+36000000)) //세션 만료시간 10시간
.signWith(SignatureAlgorithm.HS256, SALT.getBytes("UTF-8")) //서명완료
.compact();
}
//토큰 유효성 검사 메서드
public void valid(String token) throws Exception {
Jwts.parser().setSigningKey(SALT.getBytes("UTF-8")).parseClaimsJws(token);
}
}🟢 UserController를 작성하여 로그인 시 JwtUtil을 활용하여 Token을 생성해 클라이언트에게 전달해준다. 클라이언트에서 페이지 전환이 일어날 때마다 서버로 토큰을 전달하여 유효성을 검사하도록 구현한다.
//UserController
@RestController
@RequestMapping("/api/user")
@Api(tags="유저 컨트롤러")
@CrossOrigin("*")
public class UserController {
@Autowired
private GymService vService;
@Autowired
private UserService uService;
@Autowired
private ReviewService rService;
//로그인 토큰 생성용
@Autowired
private JwtUtil jwtUtil;
private static final String HEADER_AUTH = "access-token";
//=======================유저파트 CURD 구현===========================
@GetMapping("/{userId}")
@ApiOperation(value="유저 정보 조회")
public ResponseEntity<?> list(@PathVariable String userId){
User user = uService.getUser(userId);
user.setPassword("*");
if (user == null) {
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
@PostMapping("/login")
@ApiOperation(value="로그인")
public ResponseEntity<?> login(@RequestBody User user) {
User selectedUser = uService.getUser(user.getUserId());
if (selectedUser == null) {
return new ResponseEntity<String>("", HttpStatus.OK);
}
if (selectedUser.getPassword().equals(user.getPassword())) {
try {
//로그인 성공 시 토큰을 전송
return new ResponseEntity<String>(jwtUtil.createToken("userId", user.getUserId()), HttpStatus.OK);
} catch (UnsupportedEncodingException e) {
String msg = "토큰 생성 시 인코딩 에러가 발생하였습니다.";
return new ResponseEntity<String>(msg, HttpStatus.OK);
}
}
return new ResponseEntity<String>("", HttpStatus.OK);
}
//토큰 유효성 검사
@GetMapping("/validation")
@ApiOperation(value="유효성 검사")
public ResponseEntity<Boolean> validate(@RequestParam String token){
if(token != null) {
try {
jwtUtil.valid(token);
return new ResponseEntity<Boolean>(true, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<Boolean>(false, HttpStatus.OK);
}
}
return new ResponseEntity<Boolean>(false, HttpStatus.OK);
}
}🟢 로그인 인터셉터 사용이 가능하도록 WebConfig에 인터셉터 설정을 추가한다.
//WebConfig 인터셉터 설정 코드
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/api/user/login");
} 🟢 로그인 인터셉터를 구현하여, 클라이언트로부터 요청을 받을 때마다 토큰을 전달받아 유효하지않은 토큰일 경우 false를 반환한다.
@Component
public class JwtInterceptor implements HandlerInterceptor{
private static final String HEADER_AUTH = "access-token";
//인터셉터는 클라이언트 요청을 받을 경우에 작용
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader(HEADER_AUTH);
if(token != null) {
try {
jwtUtil.valid(token);
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
return false;
}
}
return false;
}
}
