211107 메인 화면 와이어프레임, 목업 개발

This commit is contained in:
jinia91
2021-11-07 23:51:36 +09:00
parent 6b72cdd70c
commit c5eb7ef895
30 changed files with 1429 additions and 73 deletions

View File

@@ -2,8 +2,10 @@ package myblog.blog;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootApplication
@EnableJpaAuditing
public class BlogApplication {
public static void main(String[] args) {

View File

@@ -0,0 +1,9 @@
package myblog.blog.article.controller;
import org.springframework.stereotype.Controller;
@Controller
public class ArticleController {
}

View File

@@ -0,0 +1,33 @@
package myblog.blog.article.domain;
import lombok.Getter;
import myblog.blog.member.doamin.Member;
import javax.persistence.*;
@Entity
@Getter
@SequenceGenerator(
name = "article_seq_generator",
sequenceName = "article_seq",
initialValue = 1, allocationSize = 50)
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "article_generator")
@Column(name = "article_id")
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
private Long hit;
private String toc;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
}

View File

@@ -0,0 +1,11 @@
package myblog.blog.article.repository;
import myblog.blog.article.domain.Article;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ArticleRepository extends JpaRepository<Article, Long> {
}

View File

@@ -0,0 +1,29 @@
package myblog.blog.article.service;
import lombok.RequiredArgsConstructor;
import myblog.blog.article.domain.Article;
import myblog.blog.article.repository.ArticleRepository;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ArticleService {
private final ArticleRepository articleRepository;
private final ModelMapper modelMapper;
public Long writeArticle(NewArticleDto newArticleDto){
Article article = modelMapper.map(newArticleDto, Article.class);
articleRepository.save(article);
return article.getId();
}
}

View File

@@ -0,0 +1,18 @@
package myblog.blog.article.service;
import myblog.blog.member.doamin.Member;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
public class NewArticleDto {
@NotBlank
private String title;
@NotBlank
private String content;
private String toc;
@NotBlank
private Long memberId;
}

View File

@@ -1,9 +1,13 @@
package myblog.blog.config;
package myblog.blog.base.config;
import lombok.RequiredArgsConstructor;
import myblog.blog.exception.LoginFailHandler;
import myblog.blog.member.doamin.Role;
import myblog.blog.member.service.Oauth2MemberService;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -13,32 +17,40 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final Oauth2MemberService oauth2MemberService;
private final LoginFailHandler loginFailHandler;
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/css/**", "/node_modules/**")
.requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/user").authenticated()
.antMatchers("/admin").access("hasRole('ROLE_ADMIN')")
.anyRequest()
.permitAll()
.antMatchers("/admin").hasRole(Role.ADMIN.name())
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID","remember-me")
.and()
.oauth2Login()
.loginPage("/login")
.failureHandler(loginFailHandler)
.userInfoEndpoint()
.userService(oauth2MemberService)
;
}
}

View File

@@ -0,0 +1,24 @@
package myblog.blog.base.domain;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public abstract class BasicEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime updatedDate;
}

View File

@@ -1,24 +0,0 @@
package myblog.blog.exception;
public class DuplicateEmailException extends RuntimeException {
public DuplicateEmailException() {
super("이미 가입한 이메일입니다.");
}
public DuplicateEmailException(String message) {
super(message);
}
public DuplicateEmailException(String message, Throwable cause) {
super(message, cause);
}
public DuplicateEmailException(Throwable cause) {
super(cause);
}
protected DuplicateEmailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -0,0 +1,23 @@
package myblog.blog.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ControllerAdvice
@Slf4j
public class ExceptionController {
// @ExceptionHandler
// public String handleRuntimeException(Principal principal, HttpServletRequest req, RuntimeException e) {
// if (principal != null) {
// log.info("'{}' requested '{}' ", principal.getName(), req.getRequestURI());
// } else {
// log.info("requested '{}'", req.getRequestURI());
// }
//
// log.error("bad request", e);
// return "";
// }
}

View File

@@ -0,0 +1,34 @@
package myblog.blog.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@Slf4j
public class LoginFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
String errMsg = "error";
if(exception instanceof OAuth2AuthenticationException){
errMsg = "duplicatedEmail";
request.setAttribute("errMsg", errMsg);
}
setDefaultFailureUrl("/login?error="+errMsg);
super.onAuthenticationFailure(request, response, exception);
}
}

View File

@@ -1,21 +1,17 @@
package myblog.blog;
package myblog.blog.main;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequiredArgsConstructor
public class MainController {
@GetMapping("/")
public String main() {
return "index";
return "main";
}

View File

@@ -2,9 +2,13 @@ package myblog.blog.member.auth;
import myblog.blog.member.doamin.Member;
import myblog.blog.member.doamin.Role;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
@@ -27,7 +31,9 @@ public class PrincipalDetails implements OAuth2User {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(member.getRole().getValue()));
return authorities;
}
@Override

View File

@@ -1,6 +1,7 @@
package myblog.blog.member.auth;
import myblog.blog.member.auth.userinfo.*;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Component;
@@ -19,7 +20,8 @@ public class UserInfoFactory {
} else if (oAuth2UserRequest.getClientRegistration().getRegistrationId().equals("naver")) {
return new NaverUserInfo(oAuth2User.getAttribute("response"));
}
else return null;
else {
throw new IllegalArgumentException("지원하지 않는 Oauth 인증 시도입니다");}
}

View File

@@ -0,0 +1,23 @@
package myblog.blog.member.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class MemberController {
@GetMapping("/login")
public String loginFrom(@RequestParam(value = "error",required = false) String error, Model model){
if(error!=null&&error.equals("duplicatedEmail")){
model.addAttribute("errMsg","이미 가입된 이메일입니다.");
}
return "login";
}
}

View File

@@ -2,8 +2,11 @@ package myblog.blog.member.doamin;
import lombok.Builder;
import lombok.Getter;
import myblog.blog.article.domain.Article;
import myblog.blog.base.domain.BasicEntity;
import javax.persistence.*;
import java.util.List;
@Entity
@SequenceGenerator(
@@ -11,10 +14,11 @@ import javax.persistence.*;
sequenceName = "MEMBER_SEQ",
initialValue = 1, allocationSize = 50)
@Getter
public class Member {
public class Member extends BasicEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
@Column(name = "member_id")
private Long id;
@Column(nullable = false)
@@ -23,7 +27,7 @@ public class Member {
@Column(nullable = false)
private String userId;
@Column(nullable = false)
@Column(nullable = false, unique = true)
private String email;
private String picUrl;
@@ -35,6 +39,9 @@ public class Member {
private String providerId;
@OneToMany(mappedBy = "member")
private List<Article> articles;
protected Member() {
}

View File

@@ -3,12 +3,12 @@ package myblog.blog.member.doamin;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
@Getter
public enum Role {
USER("ROLE_USER","일반사용자"),
ADMIN("ROLE_ADMIN","관리자");
USER("ROLE_USER"),
ADMIN("ROLE_ADMIN");
private final String value;
private final String key;
private final String title;
}

View File

@@ -1,7 +1,6 @@
package myblog.blog.member.service;
import lombok.RequiredArgsConstructor;
import myblog.blog.exception.DuplicateEmailException;
import myblog.blog.member.auth.userinfo.Oauth2UserInfo;
import myblog.blog.member.auth.UserInfoFactory;
import myblog.blog.member.repository.MemberRepository;
@@ -25,6 +24,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService {
private final MemberRepository memberRepository;
private final UserInfoFactory userInfoFactory;
// 앱 구동시 ADMIN 계정 Insert
@Value("${admin.username}")
private String adminUsername;
@Value("${admin.picUrl}")
@@ -37,6 +37,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService {
private String adminProvider;
@Override
@Transactional
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
Oauth2UserInfo userInfo =
@@ -54,7 +55,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService {
if(member == null) {
if(memberRepository.findByEmail(userInfo.getEmail()) != null)
throw new DuplicateEmailException();
throw new OAuth2AuthenticationException("duplicateEmail");
member = Member.builder()
.username(userInfo.getUserName())

View File

@@ -0,0 +1,58 @@
button img{
width: 200px;
height:50px;
}
/* Social Buttons Style */
.btnCtm {
background: none;
border: none;
background-position: 1em;
background-repeat: no-repeat;
background-size: 2em;
border-radius: 0.5em;
border: none;
color: white;
cursor: pointer;
font-size: 0.9em;
line-height: 1em;
padding: 0 2em 0 4em;
text-decoration: none;
transition: all 0.5s;
height: 50px;
width: 200px;
margin-bottom: 5px;
}
.btn-google {
background-color: #dd4b39;
background-image: url("/img/google.svg");
}
.btn-google:hover {
background-color: #e47365;
}
.btn-google:active {
background-color: #c23321;
}
.btn-facebook {
background-color: #3b5998;
background-image: url("/img/facebook.svg");
}
.btn-facebook:hover {
background-color: #4c70ba;
}
.btn-facebook:active {
background-color: #2d4373;
}
.btn-kakao{
background: none;
border: none;
}
.btn-naver{
background: none;
border: none;
}

View File

@@ -0,0 +1,333 @@
@font-face {
font-family: "IM_Hyemin-Bold";
src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2106@1.1/IM_Hyemin-Bold.woff2")
format("woff");
font-weight: normal;
font-style: normal;
}
a {
text-decoration: none;
color: rgb(65, 65, 65);
}
a:visited {
text-decoration: none;
color: rgb(65, 65, 65);
}
a:active {
text-decoration: none;
color: rgb(65, 65, 65);
}
a:hover {
text-decoration: none;
color: rgb(65, 65, 65);
}
body {
background-color: #fcf6f5;
font-family: "IM_Hyemin-Bold";
}
body::-webkit-scrollbar {
width: 15px;
}
body::-webkit-scrollbar-thumb {
background-color: rgb(233, 224, 224);
background-clip: padding-box;
border: 4px solid transparent;
border-top-left-radius: 100px;
border-bottom-right-radius: 100px;
}
body::-webkit-scrollbar-track {
background-color: #f1f1f1;
}
.offcanvas-body::-webkit-scrollbar {
width: 15px;
}
.offcanvas-body::-webkit-scrollbar-thumb {
background-color: rgb(233, 224, 224);
background-clip: padding-box;
border: 4px solid transparent;
border-top-left-radius: 100px;
border-bottom-right-radius: 100px;
}
.offcanvas-body::-webkit-scrollbar-track {
background-color: #fcf6f5;
}
.btn-toggle {
display: inline-flex;
align-items: center;
padding: 0.25rem 0.5rem;
font-weight: 600;
color: rgba(0, 0, 0, 0.65);
background-color: transparent;
border: 0;
}
.btn-toggle:hover,
.btn-toggle:focus {
color: rgba(0, 0, 0, 0.85);
background-color: #d2f4ea;
}
.btn-toggle::before {
width: 1.25em;
line-height: 0;
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
transition: transform 0.35s ease;
transform-origin: 0.5em 50%;
}
.btn-toggle[aria-expanded="true"] {
color: rgba(0, 0, 0, 0.85);
}
.btn-toggle[aria-expanded="true"]::before {
transform: rotate(90deg);
}
.btn-toggle-nav a {
display: inline-flex;
padding: 0.1875rem 0.5rem;
margin-top: 0.125rem;
margin-left: 1.25rem;
text-decoration: none;
}
.btn-toggle-nav a:hover,
.btn-toggle-nav a:focus {
background-color: #d2f4ea;
}
.navbar {
opacity: 0.98;
}
.navbar-toggler {
border: whitesmoke;
}
.fa-address-card {
font-size: 24px;
color: rgb(119, 119, 119);
}
.cover {
object-fit: cover;
}
.carousel-item {
cursor: pointer;
}
.cards-container {
max-width: 1200px;
}
.popular-card {
overflow: hidden;
cursor: pointer;
}
.card-description {
position: absolute;
top: calc(100% - 50px);
height: 50px;
overflow: hidden;
background: rgb(56, 55, 55);
color: aliceblue;
width: 100%;
opacity: 0.8;
transition-property: all;
transition-duration: 0.7s;
}
.card-des-overlay {
position: absolute;
overflow: hidden;
background: rgb(56, 55, 55);
color: aliceblue;
width: 100%;
height: 100%;
top: 100%;
opacity: 0.8;
transition-property: all;
transition-duration: 0.7s;
}
.popular-card:hover .card-des-overlay {
top: 0%;
}
.popular-card:hover .card-description {
top: 100%;
}
.recent-card-text {
overflow: hidden;
position: relative;
line-height: 1.2em;
max-height: 3.6em;
margin-right: -1em;
}
.recent-card-text:before {
content: "...";
position: absolute;
right: 4px;
bottom: 0;
}
.recent-card-text:after {
content: "";
position: absolute;
right: 4px;
width: 1em;
height: 1em;
margin-top: 0.2em;
background: white;
}
.recent-card {
transition-property: all;
transition-duration: 1s;
}
.recent-card:hover {
box-shadow: 0px 4px 8px rgba(38, 38, 38, 0.2);
top: -4px;
border: 1px solid #cccccc;
background-color: white;
cursor: pointer;
}
.arrow-up {
position: fixed;
bottom: 50px;
right: 50px;
font-size: 50px;
border-radius: 100%;
border: white;
width: 70px;
height: 70px;
color: white;
background-color: rgb(241, 226, 89);
z-index: 5;
opacity: 0.7;
}
.sidebar {
position: fixed;
height: 100vh;
z-index: 1000;
}
.sidebar::-webkit-scrollbar {
width: 15px;
}
.sidebar::-webkit-scrollbar-thumb {
background-color: rgb(233, 224, 224, 0.7);
background-clip: padding-box;
border: 4px solid transparent;
border-top-left-radius: 100px;
border-bottom-right-radius: 100px;
}
.sidebar::-webkit-scrollbar-track {
background-color: #f1f1f1;
}
@media (max-width: 767.98px) {
small {
font-size: 10px;
}
.arrow-up {
width: 50px;
height: 50px;
font-size: 30px;
line-height: 50px;
right: 16px;
bottom: 16px;
}
}
/* list */
.pagination-outer {
text-align: center;
}
.pagination {
display: inline-flex;
}
.pagination li {
margin: 0 5px;
}
.pagination li a.page-link {
color: #999;
background: #e7e7e7;
font-size: 16px;
font-weight: 700;
line-height: 30px;
height: 30px;
width: 30px;
padding: 0;
border: none;
border-radius: 5px;
display: block;
position: relative;
}
.pagination li.active a.page-link,
.pagination li a.page-link:hover,
.pagination li.active a.page-link:hover,
.pagination li a.page-link:focus {
color: rgb(12, 11, 11);
background: linear-gradient(#f0f5b0, #f8fae3);
border: none;
}
@media only screen and (max-width: 480px) {
.pagination {
font-size: 0;
display: block;
}
.pagination li {
margin: 5px 0;
display: inline-block;
}
}
.form-color {
background-color: #e0dedd;
}
.form-control {
border-radius: 25px;
height: fit-content;
resize: none;
}
.form-control:focus {
color: #495057;
background-color: #fcf6f5;
border-color: #35b69f;
outline: 0;
box-shadow: none;
text-indent: 10px;
}
.comment-text {
font-size: 13px;
}
.comment-reply {
background-color: #e0dedd;
}
.user-feed {
font-size: 14px;
margin-top: 12px;
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="35px" height="35px" viewBox="0 0 300 300" enable-background="new 0 0 266.893 266.895"
xml:space="preserve">
<path id="f" fill="#FFFFFF" d="M182.409,262.307v-99.803h33.499l5.016-38.895h-38.515V98.777c0-11.261,3.127-18.935,19.275-18.935
l20.596-0.009V45.045c-3.562-0.474-15.788-1.533-30.012-1.533c-29.695,0-50.025,18.126-50.025,51.413v28.684h-33.585v38.895h33.585
v99.803H182.409z"/>
</svg>

After

Width:  |  Height:  |  Size: 761 B

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="35px" height="35px" viewBox="10 10 110.658 110.646" enable-background="new 0 0 134.658 131.646"
xml:space="preserve">
<g>
<g>
<path fill="#FFFFFF" d="M70.479,71.845l-3.983-3.093c-1.213-1.006-2.872-2.334-2.872-4.765c0-2.441,1.659-3.993,3.099-5.43
c4.64-3.652,9.276-7.539,9.276-15.73c0-8.423-5.3-12.854-7.84-14.956h6.849l7.189-4.517H60.418
c-5.976,0-14.588,1.414-20.893,6.619c-4.752,4.1-7.07,9.753-7.07,14.842c0,8.639,6.633,17.396,18.346,17.396
c1.106,0,2.316-0.109,3.534-0.222c-0.547,1.331-1.1,2.439-1.1,4.32c0,3.431,1.763,5.535,3.317,7.528
c-4.977,0.342-14.268,0.893-21.117,5.103c-6.523,3.879-8.508,9.525-8.508,13.51c0,8.202,7.731,15.842,23.762,15.842
c19.01,0,29.074-10.519,29.074-20.932C79.764,79.709,75.344,75.943,70.479,71.845z M56,59.107
c-9.51,0-13.818-12.294-13.818-19.712c0-2.888,0.547-5.87,2.428-8.199c1.773-2.218,4.861-3.657,7.744-3.657
c9.168,0,13.923,12.404,13.923,20.382c0,1.996-0.22,5.533-2.762,8.09C61.737,57.785,58.762,59.107,56,59.107z M56.109,103.65
c-11.826,0-19.452-5.657-19.452-13.523c0-7.864,7.071-10.524,9.504-11.405c4.64-1.561,10.611-1.779,11.607-1.779
c1.105,0,1.658,0,2.538,0.111c8.407,5.983,12.056,8.965,12.056,14.629C72.362,98.542,66.723,103.65,56.109,103.65z"/>
<polygon fill="#FFFFFF" points="98.393,58.938 98.393,47.863 92.923,47.863 92.923,58.938 81.866,58.938 81.866,64.469
92.923,64.469 92.923,75.612 98.393,75.612 98.393,64.469 109.506,64.469 109.506,58.938 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
ㅎㅇ
<div sec:authorize="isAuthenticated()">
<div th:href="@{/profile/{userId}(userId=${#authentication.name})}" th:text="${#authentication.name} + '님'">
아이디</div>
</div>
<a sec:authorize="!isAuthenticated()" href="/oauth2/authorization/google">구글 로그인</a>
<a sec:authorize="!isAuthenticated()" href="/oauth2/authorization/facebook">페이스북 로그인</a>
<a sec:authorize="!isAuthenticated()" href="/oauth2/authorization/naver">네이버 로그인</a>
<a sec:authorize="!isAuthenticated()" href="/oauth2/authorization/kakao">카카오 로그인</a>
<a sec:authorize="isAuthenticated()" href="/logout">로그아웃</a>
</body>
</html>

View File

@@ -0,0 +1,294 @@
<!DOCTYPE html>
<html th:fragment="layout (title, content, style)"
lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title th:replace="${title}">Jinia's Log</title>
<!-- SEO -->
<meta name="description" content=""/>
<meta name="keyword" content=""/>
<meta name="author" content="jinia"/>
<meta name="viewport" content="width=device-width, user-scalable = no, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<!-- OPEN GRAPH(FACEBOOK, LINKEDIN) -->
<meta property="og:type" content=""/>
<meta property="og:description" content=""/>
<meta property="og:title" content=""/>
<meta property="og:image" content=""/>
<meta property="og:url" content=""/>
<meta property="og:site_name" content=""/>
<!-- twitter -->
<meta property="twitter:card" content=""/>
<meta property="twitter:title" content=""/>
<meta property="twitter:description" content=""/>
<meta property="twitter:image" content=""/>
<meta property="twitter:url" content=""/>
<meta property="twitter:creator" content=""/>
<link rel="icon" href=""/>
<link rel="apple-touch-icon" href=""/>
<link rel="short icon" type="image/x-icon" href=""/>
<!-- CSS RESET -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"/>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/mainCss.css"/>
<link th:replace="${style}">
<script src="https://kit.fontawesome.com/233840a552.js" crossorigin="anonymous"></script>
</head>
<body>
<header class="fixed-top d-xxl-none p-0">
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasMenu"
aria-controls="offcanvasMenu">
<i class="fas fa-bars"></i>
</button>
<div id="nav-brand">
<a href="#">
<h4>Blog</h4>
</a>
</div>
<div id="nav-login" sec:authorize="!isAuthenticated()"><a th:href="@{/login}"><i
class="far fa-address-card"></i></a></div>
<div sec:authorize="isAuthenticated()">
<span sec:authorize="isAuthenticated()" th:text="${#authentication.name} + '님'"></span>
<a th:href="@{/article/write}"><span sec:authorize="hasRole('ADMIN')" style="font-size: 21px"><i class="fas fa-edit"></i></span></a>
</div>
</div>
</nav>
<!-- offcanvas s -->
<aside class="offcanvas offcanvas-start " data-bs-scroll="true" style="max-width: 300px;" tabindex="-1"
id="offcanvasMenu"
aria-labelledby="offcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title">BLOG</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body bg-white">
<div href="#" class="d-flex align-items-center pb-3 mb-3 link-dark border-bottom">
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="검색해" aria-label="Search">
<button class="btn btn-outline-success" type="submit"><i class="fas fa-search"></i></button>
</form>
</div>
<ul class="list-unstyled ps-0">
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#home-collapse" aria-expanded="true">
Link
</button>
<div class="collapse show" id="home-collapse">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">
<i class="fab fa-github" style="font-size: 20px;"></i>&nbsp Github
</a></li>
</ul>
</div>
</li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#dashboard-collapse1" aria-expanded="true">
카테고리
</button>
<div class="collapse show" id="dashboard-collapse1">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
<li><a href="#" class="link-dark rounded">카테고리2</a></li>
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
</div>
</li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#dashboard-collapse2" aria-expanded="true">
카테고리
</button>
<div class="collapse show" id="dashboard-collapse2">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
<li><a href="#" class="link-dark rounded">카테고리2</a></li>
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
</div>
</li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#dashboard-collapse3" aria-expanded="true">
카테고리
</button>
<div class="collapse show" id="dashboard-collapse3">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
<li><a href="#" class="link-dark rounded">카테고리2</a></li>
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
</div>
</li>
<li class="border-top my-3"></li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#account-collapse" aria-expanded="true">
Account
</button>
<div class="collapse show" id="account-collapse">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li sec:authorize="!isAuthenticated()"><a href="#" th:href="@{/login}"
class="link-dark rounded">로그인</a></li>
<li sec:authorize="isAuthenticated()" th:text="${#authentication.name} + '님 환영합니다'"></li>
<li sec:authorize="isAuthenticated()">
<form method="post" id="logoutOC" name="logoutOC" th:action="@{/logout}">
<a href="#" class="nav-link active" onclick="document.logout.submit()">로그아웃</a>
</form>
</li>
</ul>
</div>
</li>
</ul>
</div>
</aside>
<!-- offcanvass e -->
</header>
<!-- 실제 body s -->
<section class="row g-0 row-cols-xxl-2 g-xxl-0 p-0 ">
<!-- sidebar-xxl -->
<div class="bg-white sidebar col-xxl-2 d-none d-xxl-block p-0 overflow-auto">
<div class="p-4 sidebar-inner">
<div class="m-4 sidebar-header">
<a href="#">
<h5 class="text-black-50">BLOG</h5>
</a>
<a href="#">
<h5 class="text-black-50">ABOUT ME</h5>
</a>
</div>
<div href="#" class="d-flex align-items-center pb-3 mb-3 link-dark border-bottom">
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="검색해" aria-label="Search">
<button class="btn btn-outline-success" type="submit"><i class="fas fa-search"></i></button>
</form>
</div>
<ul class="list-unstyled ps-0">
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#home-collapse-xl" aria-expanded="true">
Link
</button>
<div class="collapse show" id="home-collapse-xl">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">
<i class="fab fa-github" style="font-size: 20px;"></i>&nbsp Github
</a></li>
</ul>
</div>
</li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#dashboard-collapse1-xl" aria-expanded="true">
카테고리
</button>
<div class="collapse show" id="dashboard-collapse1-xl">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
<li><a href="#" class="link-dark rounded">카테고리2</a></li>
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
</div>
</li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#dashboard-collapse2-xl" aria-expanded="true">
카테고리
</button>
<div class="collapse show" id="dashboard-collapse2-xl">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
<li><a href="#" class="link-dark rounded">카테고리2</a></li>
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
</div>
</li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#dashboard-collapse3-xl" aria-expanded="true">
카테고리
</button>
<div class="collapse show" id="dashboard-collapse3-xl">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
<li><a href="#" class="link-dark rounded">카테고리2</a></li>
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
</div>
</li>
<li class="border-top my-3"></li>
<li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse"
data-bs-target="#account-collapse-xl" aria-expanded="true">
Account
</button>
<div class="collapse show" id="account-collapse-xl">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li sec:authorize="!isAuthenticated()"><a th:href="@{/login}"
class="link-dark rounded">로그인</a></li>
<li sec:authorize="isAuthenticated()" th:text="${#authentication.name} + '님 환영합니다'"></li>
<li sec:authorize="isAuthenticated()">
<div class="link-dark rounded">
<form method="post" id="logout" name="logout" th:action="@{/logout}">
<a href="#" class="nav-link active" onclick="document.logout.submit()">로그아웃</a>
</form>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
<!-- sidebar e -->
<!-- xxl dummy -->
<div class="bg-white col-xxl-2 d-none d-xxl-block p-0">
</div>
<!-- -->
<!-- sections -->
<div class="col-xxl-10 p-0 sections-container">
<section th:replace="${content}"></section>
<!-- sections e -->
<footer class="footer bg-light">
<div class="container text-center p-2">
<h5><span class="text-muted">Copyright ©Jinia</span></h5>
</div>
</footer>
</div>
</section>
<!-- body e -->
<button class="arrow-up">
<i class="fas fa-arrow-up"></i>
</button>
<!-- js -->
<script src="/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<!-- -->
</body>
</html>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html th:replace="~{layout/layout.html :: layout(~{::title}, ~{::section}, ~{::link})}"
lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns="http://www.w3.org/1999/html">
<head>
<title>Jinia's Log - 로그인</title>
<link rel="stylesheet" href="/css/login.css"/>
</head>
<body>
<section>
<div style="margin-bottom: 300px"></div>
<div class="container">
<div class="row">
<P class="text-center field-error mt-3" id="capsAlert" th:text="${errMsg}">로그인 오류</P>
<div class="col">
<a href="/oauth2/authorization/google">
<button class="btnCtm btn-google">구글 로그인</button>
</a>
</div>
<div class="col">
<a href="/oauth2/authorization/facebook">
<button class="btnCtm btn-facebook">페이스북 로그인</button>
</a>
</div>
<div class="col">
<a href="/oauth2/authorization/kakao">
<button class="btn-kakao"><img src="/img/kakao.png"></button>
</a>
</div>
<div class="col">
<a href="/oauth2/authorization/naver">
<button class="btn-naver"><img src="/img/naver.png"></button>
</a>
</div>
</div>
</div>
<div style="margin-bottom: 300px"></div>
</section>
</body>
</html>

View File

@@ -0,0 +1,332 @@
<!DOCTYPE html>
<html th:replace="~{layout/layout.html :: layout(~{::title}, ~{::section}, ~{::link})}"
lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>Jinia's Log</title>
<link rel="stylesheet" href=""/>
</head>
<body>
<section>
<div class="main">
<div id="carouselExampleCaptions" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-indicators">
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="0" class="active"
aria-current="true" aria-label="Slide 0"></button>
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="1"
aria-label="Slide 1"></button>
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="2"
aria-label="Slide 2"></button>
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="3"
aria-label="Slide 3"></button>
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="4"
aria-label="Slide 4"></button>
<button type="button" data-bs-target="#carouselExampleCaptions" data-bs-slide-to="5"
aria-label="Slide 5"></button>
</div>
<div class="carousel-inner">
<div class="carousel-item active">
<a href="#">
<img src="https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg"
class="w-100 vh-100 cover" alt="...">
<div class="card-img-overlay text-white text-center row justify-content-center align-content-center">
<h3 class="">첫번째 이미지</h3>
<p class="text-truncate w-50">블로그 아티클 헤으응</p>
</div>
</a>
</div>
<div class="carousel-item">
<a href="#">
<img src="https://cdn.pixabay.com/photo/2021/09/27/14/39/paris-6661136_1280.jpg"
class="w-100 vh-100 cover" alt="...">
<div class="card-img-overlay text-white text-center row justify-content-center align-content-center">
<h3>두번째 이미지</h3>
<p class="text-truncate w-50">블로그 아티클 헤으응2</p>
</div>
</a>
</div>
<div class="carousel-item">
<a href="#">
<img src="https://cdn.pixabay.com/photo/2021/10/23/16/31/italy-6735340_1280.jpg"
class="w-100 vh-100 cover" alt="...">
<div class="card-img-overlay text-white text-center row justify-content-center align-content-center">
<h3>세번째 이미지</h3>
<p class="text-truncate w-50">블로그 아티클 헤으응3</p>
</div>
</a>
</div>
<div class="carousel-item">
<a href="#">
<img src="https://cdn.pixabay.com/photo/2021/10/13/15/09/water-6706894_1280.jpg"
class="w-100 vh-100 cover" alt="...">
<div class="card-img-overlay text-white text-center row justify-content-center align-content-center">
<h3>네번째 이미지</h3>
<p class="text-truncate w-50">블로그 아티클
헤으응31!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!헤으응31!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!헤으응31!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
</p>
</div>
</a>
</div>
<div class="carousel-item">
<a href="#">
<img
src="https://media.istockphoto.com/photos/matrix-background-with-the-green-numbers-picture-id539244598"
class="w-100 vh-100 cover" alt="...">
<div class="card-img-overlay text-white text-center row justify-content-center align-content-center">
<h3>다섯번째 이미지</h3>
<p class="text-truncate w-50">블로그 아티클 헤으응3</p>
</div>
</a>
</div>
<div class="carousel-item">
<a href="#">
<img src="https://cdn.pixabay.com/photo/2021/01/01/21/31/halloween-5880068_1280.jpg"
class="w-100 vh-100 cover" alt="...">
<div class="card-img-overlay text-white text-center row justify-content-center align-content-center">
<h3>여섯번째 이미지</h3>
<p class="text-truncate">블로그 아티클 헤으응3</p>
</div>
</a>
</div>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleCaptions"
data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#carouselExampleCaptions"
data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
</div>
<div class="mt-5 pt-2 popular-card-section">
<div class="cards-container container p-0">
<div class=" row g-0 row-cols-2 row-cols-md-3">
<div class="col g-1 g-md-4 ">
<a href="#">
<div class=" card ratio ratio-1x1 popular-card " style="background-image: url(https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg);
background-size: cover;">
<div class="card-description">
<p>타이틀</p>
</div>
<div class="card-des-overlay">설명</div>
</div>
</a>
</div>
<div class="col g-1 g-md-4">
<a href="#">
<div class="card ratio ratio-1x1 popular-card " style="background-image: url(https://cdn.pixabay.com/photo/2021/09/27/14/39/paris-6661136_1280.jpg);
background-size: cover;">
<div class="card-description">
<p>타이틀</p>
</div>
<div class="card-des-overlay">설명</div>
</div>
</a>
</div>
<div class="col g-1 g-md-4">
<a href="#">
<div class="card ratio ratio-1x1 popular-card " style="background-image: url(https://cdn.pixabay.com/photo/2021/10/23/16/31/italy-6735340_1280.jpg);
background-size: cover;">
<div class="card-description">
<p>타이틀</p>
</div>
<div class="card-des-overlay">설명</div>
</div>
</a>
</div>
<div class="col g-1 g-md-4">
<a href="#">
<div class="card ratio ratio-1x1 popular-card " style="background-image: url(https://cdn.pixabay.com/photo/2021/10/13/15/09/water-6706894_1280.jpg);
background-size: cover;">
<div class="card-description">
<p>타이틀</p>
</div>
<div class="card-des-overlay">설명</div>
</div>
</a>
</div>
<div class="col g-1 g-md-4">
<a href="#">
<div class="card ratio ratio-1x1 popular-card " style="background-image: url(https://media.istockphoto.com/photos/matrix-background-with-the-green-numbers-picture-id539244598);
background-size: cover;">
<div class="card-description">
<p>타이틀</p>
</div>
<div class="card-des-overlay">설명</div>
</div>
</a>
</div>
<div class="col g-1 g-md-4">
<a href="#">
<div class="card ratio ratio-1x1 popular-card " style="background-image: url(https://cdn.pixabay.com/photo/2021/01/01/21/31/halloween-5880068_1280.jpg);
background-size: cover;">
<div class="card-description">
<p>타이틀</p>
</div>
<div class="card-des-overlay">설명</div>
</div>
</a>
</div>
</div>
</div>
</div>
<hr class="py-1 mt-5" style="color: rgb(212, 200, 184);">
<div class="recent-cards mt-5">
<div class="cards-container container p-0">
<h1 class="text-center">최신 포스팅</h1>
<hr>
<div class="card mb-3 recent-card">
<a href="#">
<div class="row g-0">
<div class="col-3">
<div class="ratio ratio-1x1" style="background-image: url(https://cdn.pixabay.com/photo/2021/09/27/14/39/paris-6661136_1280.jpg);
background-size: cover;"></div>
</div>
<div class="col-9 row row-cols-1 align-self-center">
<h3 class="card-title col mb-3 text-truncate">글 제목</h3>
<p class="d-none d-md-block col recent-card-text">대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용대충
존나
내용대충 존나 긴대충 긴대충 긴대충 긴대충 긴대충 긴대충 긴대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용</p>
<p class="col mb-0"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</a>
</div>
<div class="card mb-3 recent-card">
<a href="#">
<div class="row g-0">
<div class="col-3">
<div class="ratio ratio-1x1" style="background-image: url(https://media.istockphoto.com/photos/matrix-background-with-the-green-numbers-picture-id539244598);
background-size: cover;"></div>
</div>
<div class="col-9 row row-cols-1 align-self-center">
<h3 class="card-title col mb-3 text-truncate">글 제목</h3>
<p class="d-none d-md-block col recent-card-text">대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용대충
존나
내용대충 존나 긴
내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충
존나 긴 내용대충 존나 긴
내용대충
존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용
</p>
<p class="col mb-0"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</a>
</div>
<div class="card mb-3 recent-card">
<a href="#">
<div class="row g-0">
<div class="col-3">
<div class="ratio ratio-1x1" style="background-image: url(https://media.istockphoto.com/photos/matrix-background-with-the-green-numbers-picture-id539244598);
background-size: cover;"></div>
</div>
<div class="col-9 row row-cols-1 align-self-center">
<h3 class="card-title col mb-3 text-truncate">글 제목</h3>
<p class="d-none d-md-block col recent-card-text">대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용대충
존나
내용대충 존나 긴
내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충
존나 긴 내용대충 존나 긴
내용대충
존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용
</p>
<p class="col mb-0"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</a>
</div>
<div class="card mb-3 recent-card">
<a href="#">
<div class="row g-0">
<div class="col-3">
<div class="ratio ratio-1x1" style="background-image: url(https://media.istockphoto.com/photos/matrix-background-with-the-green-numbers-picture-id539244598);
background-size: cover;"></div>
</div>
<div class="col-9 row row-cols-1 align-self-center">
<h3 class="card-title col mb-3 text-truncate">글 제목</h3>
<p class="d-none d-md-block col recent-card-text">대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용대충
존나
내용대충 존나 긴
내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충
존나 긴 내용대충 존나 긴
내용대충
존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용
</p>
<p class="col mb-0"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</a>
</div>
<div class="card mb-3 recent-card">
<a href="#">
<div class="row g-0">
<div class="col-3">
<div class="ratio ratio-1x1" style="background-image: url(https://cdn.pixabay.com/photo/2021/01/01/21/31/halloween-5880068_1280.jpg);
background-size: cover;"></div>
</div>
<div class="col-9 row row-cols-1 align-self-center">
<h3 class="card-title col mb-3 text-truncate">글 제목</h3>
<p class="d-none d-md-block col recent-card-text">대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용대충
존나
내용대충 존나 긴
내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충
존나 긴 내용대충 존나 긴
내용대충
존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴 내용대충 존나 긴
내용대충 존나 긴 내용
</p>
<p class="col mb-0"><small class="text-muted update">Last updated 3 mins ago</small></p>
</div>
</div>
</a>
</div>
</div>
</div>
</section>
</body>
</html>

View File

@@ -0,0 +1,33 @@
package myblog.blog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class MainControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(roles = "ADMIN")
public void 권한테스트() throws Exception {
// given
// when
ResultActions admin = mockMvc.perform(get("/admin"));
// then
admin.andExpect(status().isOk());
}
}

View File

@@ -0,0 +1,37 @@
package myblog.blog.member.service;
import myblog.blog.member.doamin.Member;
import myblog.blog.member.doamin.Role;
import myblog.blog.member.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
@SpringBootTest
class Oauth2MemberServiceTest {
@Autowired
Oauth2MemberService oauth2MemberService;
@Autowired
MemberRepository memberRepository;
@Test
public void 권한테스트() throws Exception {
// given
Optional<Member> byId = memberRepository.findById(1L);
Member admin = byId.get();
// when
// then
assertThat(admin.getRole().toString()).isEqualTo(Role.ADMIN.toString());
}
}