일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- git 컨벤션
- 최대공약수와 최소공배수
- stringbuilder의 reverse()
- Git사용법
- 스프링
- isuppercase()
- string
- replaceAll()
- ineer join
- islowercase()
- 베주계수
- 스프링환경설정
- 자바 스트링
- 자바 유클리드
- 자바 최소공배수
- StringBuilder
- 자바 최대공약수
- 동일성과 동등성
- 프로그래머스 레벨1
- 유클리드호제법
- 모던자바
- 래퍼타입
- toLowerCase()
- sql 데이터형 변환
- while과 two-pointer
- 최소공배수
- addDoc
- string과 stringbuilder
- 최대공약수
- 스프링뼈대
- Today
- Total
주노 님의 블로그
20240825 주말에도 나와버린 나의 과제 작성기 본문
최선을 다하면되는거야~
숙련주차 개인 과제 구현
3단계
페이지네이션을 위해 Pageable과 Sort인터페이스를 사용하였다
public List<TaskResponseDto> getTasks(int page, int size)
{
Sort sort = Sort.by(Sort.Direction.DESC, "modifiedAt");
Pageable pageable = PageRequest.of(page-1 ,size, sort);
Page<Task> tasks = taskRepository.findAll(pageable);
List<TaskResponseDto> taskResponseDtos = new ArrayList<>();
for(Task task : tasks.getContent())
{
taskResponseDtos.add(new TaskResponseDto(task));
}
if(taskResponseDtos.isEmpty())
{
new IllegalArgumentException("일정 목록이 없습니다.");
}
return taskResponseDtos;
}
page와 size를 리퀘스트파람으로 받아서 페이지와 사이즈를 정한다
그리고 페이지가 0부터 시작한다고 며칠전 튜터님께 들었던 기억이났다
그래서 바로.. 줍줍 하였다.
jdbcTemplate와 비교하여 엄청 편해졌다
물론 jdbcTemplate도 어려운건 아니었지만 귀찮은 코드긴 했었다.
하지만 jpa를 사용함으로써 엄청 간편해지고 가독성이 좋은 코드로 바뀐것같다고 생각한다.
참고자료
https://zumsim.tistory.com/59
4단계
일정 전체 삭제.
나는 진짜 삭제가아닌 삭제된것처럼.. 보이게하는 boolean 변수를 이용하여 일괄 수정을 진행한다
@OneToMany(mappedBy = "task", cascade = CascadeType.PERSIST)
private List<Reply> replyList = new ArrayList<>();
cascade 타입을 PERSIST로 정한다.
사실 REMOVE가 맞는데 진짜 REMOVE는 사용을 못하니
근디 cascade 타입을 persist로 지정해도 delete를 수행하는건 수동인데...
softDelete는 이걸 구현할 수 있나?
힘들거같아서 branch를 따로 파서 작업하기로 했다.
진짜 삭제를 원할경우 delete로 하면된다
옛날에 jpa프로젝트할때 그걸로 교수님한테 피드백 받은적 있걸랑
@Transactional
public void deleteTask(Long id)
{
Task task = findTask(id);
if(task.isDeleteStatus())
{
throw new IllegalArgumentException("이미 삭제된 일정은 다시 삭제할 수 없습니다");
}
task.delete();
for(Reply reply : task.getReplyList())
{
reply.delete();
}
}
그리고 위처럼 구현해준다
트랜잭션을 걸어줘야 상태 변경이 이루어진다.
+실패시 롤백이 될수도 있으니..!
5단계
엄청난 코드 변경과 수정을 요하는 5단계
차라리 처음부터 다시짜는게 나을정도지만..
이리저리 수정해줘야한다
유저 엔티티를 추가해주면서
중간 테이블인
매니저 테이블을 추가해주고
유저와 매니저 일정간의 관계를 수정
일정 엔티티를 수정해주고
dto를 각각의 연관관계에 맞게 수정해주고.(일정을 추가하면 그에 맞게 회원 id랑 이어줘야함)
service는 삭제 로직만 바꿔주면된다
베베꼬인 관계
살짝 월요일날 물어볼것..
만들긴 했는데.. 정상적으로 돌아가긴하는데..
맞는지 코드를 짜면서도 이 관계가 맞는지 확신이 안든다 ㅠㅠ
단계6
일정 단건 조회시 담당 유저 정보가 떠야한다면
private List<MemberResponseDto> managers = new ArrayList<>();
public TaskResponseDto(Task task)
{
this.id = task.getId();
this.title = task.getTitle();
this.contents = task.getContents();
this.registerAt = task.getRegisterAt();
this.modifiedAt = task.getModifiedAt();
this.delete_status = task.isDeleteStatus();
this.memberId=task.getMember().getId();
this.managers = task.getManagerList().stream()
.map(manager -> new MemberResponseDto(manager.getMember()))
.collect(Collectors.toList());
}
taskResponseDto를 수정한다
managers 정보를 위해 list에서 member정보를 가져와야하고
멤버 정보를 불러온다.
그러면 매니저들의 정보를 불러 올 수 있게된다
잠깐.
dto를 따로 빼야할것같다.
@Getter
@NoArgsConstructor
public class MemberManagerResponseDto
{
private Long id;
private String name;
private String email;
public MemberManagerResponseDto(Member member)
{
this.id =member.getId();
this.name =member.getName();
this.email =member.getEmail();
}
}
필요한 정보만을 담은 dto로 변경했다 간단하게 id name email만 담은 정보면 된다
일정 전체 조회시 지연로딩을 이용하라는디
package com.sparta.springadvancedpersonalproject.entity;
import com.sparta.springadvancedpersonalproject.dto.request.TaskCreateRequestDto;
import com.sparta.springadvancedpersonalproject.dto.request.TaskUpdateRequestDto;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@NoArgsConstructor
@Table(name = "task")
public class Task extends Timestamped
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String title;
@Column(nullable = false, length = 200)
private String contents;
@Column
private boolean deleteStatus = false;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@OneToMany(mappedBy = "task")
private List<Reply> replyList = new ArrayList<>();
@OneToMany(mappedBy = "task")
private List<Manager> managerList = new ArrayList<>();
public Task(TaskCreateRequestDto taskCreateRequestDto)
{
this.title = taskCreateRequestDto.getTitle();
this.contents = taskCreateRequestDto.getContents();
}
public Task(String title, String contents, Member member) {
this.title = title;
this.contents = contents;
this.member = member;
}
public void update(TaskUpdateRequestDto taskUpdateRequestDto)
{
this.title = taskUpdateRequestDto.getTitle();
this.contents = taskUpdateRequestDto.getContents();
}
public void delete()
{
this.deleteStatus = true;
}
}
단계 7
유저에 비밀번호를 추가하였다
그에 맞는 dto를 수정하였다
어떻게 비밀번호 암호화를 해야할까?
@Component
public class PasswordEncoder {
public String encode(String rawPassword) {
return BCrypt.withDefaults().hashToString(BCrypt.MIN_COST, rawPassword.toCharArray());
}
public boolean matches(String rawPassword, String encodedPassword) {
BCrypt.Result result = BCrypt.verifyer().verify(rawPassword.toCharArray(), encodedPassword);
return result.verified;
}
}
일단 password encoder가 주어졌다
로우 패스워드를 encode하면 암호화가되어서 나타남
/**
* 회원을 등록합
* @param memberCreateRequestDto 회원 이름과 이메일을 받습니다
* @return 회원 아이디와 회원이름, 회원이메일, 삭제여부, 등록일자, 수정일자를 반환합니다.
*/
@PostMapping("/member")
public ResponseEntity<MemberResponseDto> signupMember(@RequestBody MemberCreateRequestDto memberCreateRequestDto, HttpServletResponse res) {
MemberResponseDto responseDto = memberService.signupMember(memberCreateRequestDto, res);
return new ResponseEntity<>(responseDto, HttpStatus.CREATED);
}
원래는 createMember이었지만 상황에 맞게 signup으로 변환하였다
또 쿠키에 사용될 수 있게 HttpServletResponse을 받는다
아참 튜터님이 단축어 쓰지말랫는데
public MemberResponseDto signupMember(MemberCreateRequestDto memberCreateRequestDto, HttpServletResponse response)
{
String password = passwordEncoder.encode(memberCreateRequestDto.getPassword());
Member member = new Member(memberCreateRequestDto.getName(), password, memberCreateRequestDto.getEmail());
memberRepository.save(member);
String token = jwtUtil.createToken(member.getId());
jwtUtil.addJwtToCookie(token, response);
MemberResponseDto responseDto = new MemberResponseDto(member);
return responseDto;
}
급하게 response로 바꾸고
위 코드에서 password를 받은 비밀번호를 넣어 인코딩한다
당연 저건 매치로 로그인할때 쓰면될듯
그리고 멤버를 만들때는 암호화된 암호를 넣어주고
토큰을 받아주기위해 jwtUtil에 있는 createToken으로 멤버아이디를 넣었다
util은 아직은 유저 권한이 필요없어서 권한부분은 자름
그러니까 담아서 주긴했다
어차피 bearer이후로 잘릴거니까 받아올수 있긴함!
패스워드도 암호처럼 들어간것을 볼 수 있다
추가 공부한 자료
https://brunch.co.kr/@jinyoungchoi95/1
단계 8
아까 받은 암호화된 비밀번호와 입력된 비밀번호를 매치시켜서
맞으면 로그인하는 기능을 구현해보자.
@Column(name = "email", unique = true, nullable = false, length = 100)
private String email;
일단 이메일로 로그인하니 unique를 ture로 체크해서 중복이 안되게 하자
테이블을 삭제하고 하이버네이트가 다시 만들게 하자
/**
* 회원 로그인을 진행합니다.
* @param memberLoginRequestDto 로그인정보는 이메일과
* @param response
* @return 반환값은 jwt값입니다.
*/
@PostMapping("/member/login")
public String loginMember(@RequestBody MemberLoginRequestDto memberLoginRequestDto, HttpServletResponse response)
{
String token = memberService.loginMember(memberLoginRequestDto, response);
return token;
}
controller에 로그인을 만들어주자
로그인 dto는 아이디와 이메일만을 받으니 따로 dto를 만들어주자
public String loginMember(MemberLoginRequestDto memberLoginRequestDto, HttpServletResponse response)
{
String email = memberLoginRequestDto.getEmail();
String password = memberLoginRequestDto.getPassword();
Member member = memberRepository.findByEmail(email);
if(member == null)
{
throw new IllegalArgumentException("회원이 존재하지 않습니다");
}
if(!passwordEncoder.matches(password, member.getPassword()))
{
throw new IllegalArgumentException("비밀번호가 일치하지 않습니다");
}
String token = jwtUtil.createToken(member.getId());
jwtUtil.addJwtToCookie(token, response);
return token;
}
회원이 없을때와 비밀번호가 맞을때를 확인하는 로직을 추가하고
로그인이 성공되었을때 토큰을 만들고 쿠키에 넣어준다.
필터를 적용했는데도 잘 되는 마법이?
알고보니 포스트맨이 쿠키를 자동으로 등록해주는 거시었다
.
이게 로그인시, 회원가입시 반환되는 토큰을 자동으로 저장하고있었다
땡큐지만 일단 필터 테스트를 위해 빼놓자
필터가 토큰이 없음을 확인하고 잡아줬다
이제 모든 단계에서
위 링크에서는 토큰이 없어도 검증이 되지않지만
다른곳에서는 토큰이없으면 안된다
그리고 이메일과 비밀번호가 다를때 responseEntity를 사용하여
상태코드를 같이 반환해보자
그리고 토큰이 없을때의 로직은 모든 서비스에 구현하기 어렵다
아니 귀찮다
그래서 우리가 아까 사용했던 authFilter에 사용해보자
if (!jwtUtil.validateToken(token)) {
throw new IllegalArgumentException("Token Error");
}
위 코드긴 한데.. 문제는 void이다.
갑자기 생각난 내용은
status를 반환한 후에
return으로 강종시키는 방법이..
인텔리제이가 편해진걸 느낀 나
참고자료
https://velog.io/@2jjong/Spring-Boot-s6xmqo77
'TIL' 카테고리의 다른 글
20240827 본캠프 32일차 TIL (0) | 2024.08.27 |
---|---|
20240826 본캠프 31일차 TIL (0) | 2024.08.26 |
20240823 본캠프 30일차 TIL (0) | 2024.08.23 |
20240822 본캠프 29일차 TIL (0) | 2024.08.22 |
20240821 본캠프 28일차 TIL (0) | 2024.08.21 |