Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- Thread
- 시스템 환경 변수 편집
- function
- 메서드 오버로딩
- 집합관계
- 상수
- JAVA기초
- 자바 멀티스레딩
- Java
- OPP개념
- 접근제어지시자
- JAVA객체지향
- 컴파일
- continue문
- break문
- 메서드
- this예약어
- 인텔리제이 기초 설정
- IntelliJ IDEA
- java변수
- 인텔리제이 한글 깨짐 해결법
- Java데이터 타입
- 형 변환
- 연관관계
- for문
- multi-threading
- 포함관계
- While
- 생성자
- 반복문
Archives
- Today
- Total
최원종의 개발 블로그
V13-4 마이페이지 소셜 이미지 출력하기 위한 코드 수정 본문

User 엔티티 코드 확인
// 머스태치 화면에서 사용할 편의 메서드 2
// OAuthProvider 값에 따라서 경로 변수를 다르게 리턴
public String getProfilePath() {
if(this.profileImage == null) {
return null;
}
// 이미지 경로가 http 로 시작 (소셜 가입)
if(this.profileImage.startsWith("http")) {
return this.profileImage;
}
// 로컬 이미지(서버 기준 경로)
return "/images/" + this.profileImage;
}
detail.mustache 추가
<!-- 회원 가입시 로컬 가입자 -->
<img class="rounded-circle" src="{{user.profilePath}}" alt="프로필이미지"
style="width:200px; height:200px"/>
<!-- 회원 가입시 소셜 가입자 -->
detail.mustache 전체코드⬇️
더보기
{{> layout/header}}
<div class="container p-5 flex-grow-1">
<div class="card">
<div class="card-header">마이프로필</div>
<div class="card-body">
<!-- 프로필 이미지 start -->
<div class="text-center mb-4">
{{#user.profileImage}}
<!-- 회원 가입시 로컬 가입자 -->
<img class="rounded-circle" src="{{user.profilePath}}" alt="프로필이미지"
style="width:200px; height:200px"/>
<!-- 회원 가입시 소셜 가입자 -->
<div class="mt-2">
<form action="/user/profile-image/delete" method="post">
<button type="submit" class="btn btn-sm btn-danger"
onclick="return confirm('프로필 사진을 삭제할까요?')">프로필 사진 삭제</button>
</form>
</div>
{{/user.profileImage}}
{{^user.profileImage}}
<div class="rounded-circle bg-secondary d-inline-flex
justify-content-center align-items-center border"
style="width:200px; height:200px;">
<span>프로필 사진 없음</span>
</div>
{{/user.profileImage}}
</div>
<!-- 프로필 이미지 end -->
<div class="mb-3">
<label class="form-label fw-bold">사용자명</label>
<p class="form-control-plaintext">{{user.username}}</p>
</div>
<div class="mb-3">
<label class="form-label fw-bold">이메일</label>
{{#user.email}}
<p class="form-control-plaintext">{{user.email}}</p>
{{/user.email}}
</div>
<div class="mb-3">
<label class="form-label fw-bold">가입일</label>
<p class="form-control-plaintext">{{user.createdAt}}</p>
</div>
<div class="mb-3">
<a href="/user/update-form" class="btn btn-primary" >회원정보 수정</a>
<a href="/board/list" class="btn btn-secondary" >게시글 목록</a>
</div>
</div>
</div>
</div>
{{> layout/footer}}
update-form.mustache 추가 코드
<img class="rounded-circle" src="{{user.profilePath}}" alt="프로필이미지"
style="width:200px; height:200px"/>
update-form.mustache 전체 코드⬇️
더보기
{{> layout/header}}
<div class="container p-5 flex-grow-1">
<div class="card">
<div class="card-header"><b>회원정보수정</b></div>
<div class="card-body">
<!-- 프로필 이미지 start -->
<div class="text-center mb-4">
{{#user.profileImage}}
<img class="rounded-circle" src="{{user.profilePath}}" alt="프로필이미지"
style="width:200px; height:200px"/>
<div class="mt-2">
<form action="/user/profile-image/delete" method="post">
<button type="submit" class="btn btn-sm btn-danger"
onclick="return confirm('프로필 사진을 삭제할까요?')">프로필 사진 삭제</button>
</form>
</div>
{{/user.profileImage}}
{{^user.profileImage}}
<div class="rounded-circle bg-secondary d-inline-flex
justify-content-center align-items-center border"
style="width:200px; height:200px;">
<span>프로필 사진 없음</span>
</div>
{{/user.profileImage}}
</div>
<!-- 프로필 이미지 end -->
<!-- 이미지 업로드 시 반드시 multipart/form-data 선언 -->
<form action="/user/update" method="post" enctype="multipart/form-data">
<div class="mb-3">
<input type="text" class="form-control" name="username" value="{{user.username}}" disabled>
</div>
<div class="mb-3">
<input type="password" class="form-control" name="password" value="" >
</div>
{{#user.email}}
<div class="mb-3">
<input type="text" class="form-control" name="email" value="{{user.email}}" disabled>
</div>
{{/user.email}}
<!-- 프로필 이미지 등록 필드(선택 사항) -->
<div class="mb-3">
<label for="profileImage" class="form-label">프로필 사진(선택사항)</label>
<input type="file" id="profileImage" class="form-control" name="profileImage" accept="image/*" >
<small class="form-text text-muted">이미지 파일만 업로드 가능합니다(JPG, PNG, GIF 등)</small>
<small class="form-text text-muted d-block mt-1">⭐ 프로필 사진을 등록하지 않아도 회원 가입 가능 </small>
</div>
<button class="btn btn-primary form-control">회원정보수정</button>
</form>
</div>
</div>
</div>
{{> layout/footer}}
UserService 에서 소셜회원가입 메서드 따로 추가
/**
* 소셜회원가입
* @param joinDTO (사용자 회원가입 요청 정보)
* @return User (저장된 사용자 정보)
*/
@Transactional
public User 소셜회원가입(UserRequest.JoinDTO joinDTO, String profileImageUrl) {
log.info("소셜 회원가입 서비스 시작");
// 회원가입시 사용자 이름 중복 체크
userRepository.findByUsername(joinDTO.getUsername()).ifPresent(user -> {
log.warn("회원가입 실패 - 중복된 사용자명 : {}", user.getUsername());
throw new Exception400("이미 존재하는 사용자 이름입니다");
});
// 코드 수정
User user = joinDTO.toEntity(profileImageUrl);
String hashPwd = passwordEncoder.encode("1234");
user.setPassword(hashPwd);
// 기본 권한 추가 (일반 사용자로 설정)
user.addRole(Role.USER);
user.setOAuthProvider(OAuthProvider.KAKAO);
return userRepository.save(user);
}
UserController 코드 수정
// 1. 인가 코드 받음 -> 2. 토큰 발급 요청(JWT - CSR)
@GetMapping("/kakao-redirect")
public String kakaoCallback(@RequestParam(name = "code") String code, HttpSession session) {
System.out.println("카카오 리다이렉트 값 확인 ");
RestTemplate restTemplate1 = new RestTemplate();
// 헤더
HttpHeaders headers1 = new HttpHeaders();
headers1.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
// 바디
// 1. 방식 - application/json;
// 2. 방식 - application/x-www-form-urlencoded;
// {key=value, key=value, key=value} -> LinkedMultiValueMap -> 장점 - URLEncoding 을 알아서 해준다.
LinkedMultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap();
multiValueMap.add("grant_type", "authorization_code");
multiValueMap.add("client_id", kakaoClientId);
multiValueMap.add("redirect_uri", "http://localhost:8080/kakao-redirect");
multiValueMap.add("code", code);
// 최신사항 : 반드시 시크릿키 body 설정
multiValueMap.add("client_secret", kakaoClientSecret);
// 순서 중요 : 바디 + 헤더 결합 ( HTTP 요청 메세지 구축)
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity(multiValueMap, headers1);
// HTTP 요청 후 응답
ResponseEntity<UserResponse.OAuthToken> response1 = restTemplate1.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
request,
UserResponse.OAuthToken.class
);
/// //////////////////////////////////////////////////////////
// 발급 받은 액세스 토큰으로 해당 사용자의 정보 요청
String accessToken = response1.getBody().getAccessToken();
RestTemplate restTemplate2 = new RestTemplate();
HttpHeaders headers2 = new HttpHeaders();
// 주의! 반드시 Bearer + "공백한칸" + 토큰
headers2.add("Authorization", "Bearer " + accessToken);
headers2.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
HttpEntity request2 = new HttpEntity(headers2);
// HTTP 요청 2
ResponseEntity<UserResponse.KakaoProfile> response2 = restTemplate2.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
request2,
UserResponse.KakaoProfile.class
);
//// 소셜 로그인 설계 방식
// 1. 최초 사용자라면 우리 서버에 회원 가입 처리
// 2. 회원 가입이 되어 있는 소셜 로그인 사용자라면 바로 로그인 처리
// 소셜 가입자 닉네임 형태 결정 난수_최원종
UserResponse.KakaoProfile.KakaoAccount.Profile profile = response2.getBody().getKakaoAccount().getProfile();
String username = profile.getNickname() + "_" + response2.getBody().getId();
User userEntity = userService.사용자이름조회(username);
if (userEntity == null) {
// 최소 사용자시 회원 자동 가입
UserRequest.JoinDTO joinDTO = new UserRequest.JoinDTO();
joinDTO.setUsername(username);
joinDTO.setEmail(null);
joinDTO.setPassword("1234");
userEntity = userService.소셜회원가입(joinDTO, profile.getProfileImageUrl());
}
// 세션 정보 저장
session.setAttribute(Define.SESSION_USER, userEntity);
return "redirect:/board/list";
}'Spring boot 입문' 카테고리의 다른 글
| V13-3 회원 가입시 출처 관리 (로컬, 소셜) (0) | 2026.06.02 |
|---|---|
| V13-2 로그인시 메서드 수정(암호화 처리) (0) | 2026.06.02 |
| V13 비밀번호 암호화 (회원 가입시 암호화 처리 ) (0) | 2026.06.02 |
| V12 - 2 카카오 소셜 로그인 (0) | 2026.05.26 |
| V12-1 Oauth 2.0 (카카오 소셜 로그인) (0) | 2026.05.26 |
