211108 아티클 도메인 개발중
This commit is contained in:
@@ -1,9 +1,40 @@
|
|||||||
package myblog.blog.article.controller;
|
package myblog.blog.article.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import myblog.blog.article.dto.NewArticleDto;
|
||||||
|
import myblog.blog.article.service.ArticleService;
|
||||||
|
import myblog.blog.member.auth.PrincipalDetails;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class ArticleController {
|
public class ArticleController {
|
||||||
|
|
||||||
|
private final ArticleService articleService;
|
||||||
|
|
||||||
|
@GetMapping("article/write")
|
||||||
|
public String writeArticleForm(NewArticleDto newArticleDto, Model model){
|
||||||
|
|
||||||
|
model.addAttribute(newArticleDto);
|
||||||
|
|
||||||
|
return "articleWriteForm";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("article/write")
|
||||||
|
public String WriteArticle(@ModelAttribute NewArticleDto newArticleDto, Authentication authentication){
|
||||||
|
|
||||||
|
PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal();
|
||||||
|
newArticleDto.setMemberId(principal.getMemberId());
|
||||||
|
|
||||||
|
Long articleId = articleService.writeArticle(newArticleDto);
|
||||||
|
|
||||||
|
return "redirect:/";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package myblog.blog.article.domain;
|
package myblog.blog.article.domain;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import myblog.blog.base.domain.BasicEntity;
|
||||||
import myblog.blog.member.doamin.Member;
|
import myblog.blog.member.doamin.Member;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
@@ -8,26 +10,37 @@ import javax.persistence.*;
|
|||||||
@Entity
|
@Entity
|
||||||
@Getter
|
@Getter
|
||||||
@SequenceGenerator(
|
@SequenceGenerator(
|
||||||
name = "article_seq_generator",
|
name = "ARTICLE_SEQ_GENERATOR",
|
||||||
sequenceName = "article_seq",
|
sequenceName = "ARTICLE_SEQ",
|
||||||
initialValue = 1, allocationSize = 50)
|
initialValue = 1, allocationSize = 50)
|
||||||
public class Article {
|
public class Article extends BasicEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "article_generator")
|
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ARTICLE_SEQ_GENERATOR")
|
||||||
@Column(name = "article_id")
|
@Column(name = "article_id")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String title;
|
private String title;
|
||||||
@Column(nullable = false)
|
@Column(nullable = false, length = 10000)
|
||||||
private String content;
|
private String content;
|
||||||
|
@Column(columnDefinition = "bigint default 0",nullable = false)
|
||||||
private Long hit;
|
private Long hit;
|
||||||
private String toc;
|
private String toc;
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "member_id")
|
@JoinColumn(name = "member_id")
|
||||||
private Member member;
|
private Member member;
|
||||||
|
private String thumbnailUrl;
|
||||||
|
|
||||||
|
protected Article() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public Article(String title, String content, String toc, Member member) {
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.toc = toc;
|
||||||
|
this.member = member;
|
||||||
|
this.hit = 0L;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package myblog.blog.article.service;
|
package myblog.blog.article.dto;
|
||||||
|
|
||||||
import myblog.blog.member.doamin.Member;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
import javax.persistence.*;
|
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
public class NewArticleDto {
|
public class NewArticleDto {
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@@ -15,4 +17,7 @@ public class NewArticleDto {
|
|||||||
@NotBlank
|
@NotBlank
|
||||||
private Long memberId;
|
private Long memberId;
|
||||||
|
|
||||||
|
private String thumbnailUrl;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,10 @@ package myblog.blog.article.service;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import myblog.blog.article.domain.Article;
|
import myblog.blog.article.domain.Article;
|
||||||
|
import myblog.blog.article.dto.NewArticleDto;
|
||||||
import myblog.blog.article.repository.ArticleRepository;
|
import myblog.blog.article.repository.ArticleRepository;
|
||||||
import org.modelmapper.ModelMapper;
|
import myblog.blog.member.doamin.Member;
|
||||||
|
import myblog.blog.member.repository.MemberRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -11,19 +13,27 @@ import org.springframework.stereotype.Service;
|
|||||||
public class ArticleService {
|
public class ArticleService {
|
||||||
|
|
||||||
private final ArticleRepository articleRepository;
|
private final ArticleRepository articleRepository;
|
||||||
private final ModelMapper modelMapper;
|
private final MemberRepository memberRepository;
|
||||||
|
|
||||||
public Long writeArticle(NewArticleDto newArticleDto){
|
public Long writeArticle(NewArticleDto articleDto) {
|
||||||
|
|
||||||
Article article = modelMapper.map(newArticleDto, Article.class);
|
Article newArticle = createNewArticleFrom(articleDto);
|
||||||
|
articleRepository.save(newArticle);
|
||||||
articleRepository.save(article);
|
return newArticle.getId();
|
||||||
|
|
||||||
return article.getId();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Article createNewArticleFrom(NewArticleDto articleDto) {
|
||||||
|
Member member =
|
||||||
|
memberRepository.findById(articleDto.getMemberId()).orElseThrow(() -> {
|
||||||
|
throw new IllegalArgumentException("작성자를 확인할 수 없습니다");
|
||||||
|
});
|
||||||
|
|
||||||
|
return Article.builder()
|
||||||
|
.title(articleDto.getTitle())
|
||||||
|
.content(articleDto.getContent())
|
||||||
|
.toc(articleDto.getToc())
|
||||||
|
.member(member)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers("/admin").hasRole(Role.ADMIN.name())
|
.antMatchers("/article/write").hasRole(Role.ADMIN.name())
|
||||||
.anyRequest().permitAll()
|
.anyRequest().permitAll()
|
||||||
|
|
||||||
.and()
|
.and()
|
||||||
|
|||||||
19
src/main/java/myblog/blog/img/domain/UploadedImg.java
Normal file
19
src/main/java/myblog/blog/img/domain/UploadedImg.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package myblog.blog.img.domain;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class UploadedImg {
|
||||||
|
|
||||||
|
private String uploadFileName;
|
||||||
|
private String storeFileName;
|
||||||
|
private String uploadUrl;
|
||||||
|
|
||||||
|
public UploadedImg(String uploadFileName, String storeFileName, String uploadUrl) {
|
||||||
|
this.uploadFileName = uploadFileName;
|
||||||
|
this.storeFileName = storeFileName;
|
||||||
|
this.uploadUrl = uploadUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/myblog/blog/img/service/ImgService.java
Normal file
59
src/main/java/myblog/blog/img/service/ImgService.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package myblog.blog.img.service;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import myblog.blog.img.domain.UploadedImg;
|
||||||
|
import org.kohsuke.github.GHRepository;
|
||||||
|
import org.kohsuke.github.GitHub;
|
||||||
|
import org.kohsuke.github.GitHubBuilder;
|
||||||
|
import org.modelmapper.ModelMapper;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ImgService {
|
||||||
|
|
||||||
|
@Value("${git.gitToken}")
|
||||||
|
private String gitToken;
|
||||||
|
@Value("${git.imgRepo}")
|
||||||
|
|
||||||
|
private String gitRepo;
|
||||||
|
@Value("${git.imgUrl}")
|
||||||
|
private String imgUrl;
|
||||||
|
|
||||||
|
private final ModelMapper modelMapper;
|
||||||
|
|
||||||
|
public UploadedImg storeImg(MultipartFile multipartFile) throws IOException {
|
||||||
|
if (multipartFile.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("이미지가 존재하지 않습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
|
||||||
|
GHRepository repository = gitHub.getRepository(gitRepo);
|
||||||
|
|
||||||
|
String originalFilename = multipartFile.getOriginalFilename();
|
||||||
|
String storeFileName = createStoreFileName(originalFilename);
|
||||||
|
|
||||||
|
repository.createContent().path("img/"+storeFileName)
|
||||||
|
.content(multipartFile.getBytes()).message("test").branch("main").commit();
|
||||||
|
|
||||||
|
return new UploadedImg(originalFilename, storeFileName, imgUrl +storeFileName+"?raw=true");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createStoreFileName(String originalFilename) {
|
||||||
|
String ext = extractExt(originalFilename);
|
||||||
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
return uuid + "." + ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractExt(String originalFilename) {
|
||||||
|
int pos = originalFilename.lastIndexOf(".");
|
||||||
|
return originalFilename.substring(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,6 +23,10 @@ public class PrincipalDetails implements OAuth2User {
|
|||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getMemberId(){
|
||||||
|
return member.getId();
|
||||||
|
}
|
||||||
|
|
||||||
// Oauth2
|
// Oauth2
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getAttributes() {
|
public Map<String, Object> getAttributes() {
|
||||||
|
|||||||
@@ -17,12 +17,15 @@ button img{
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
|
font-family: 'Noto Sans C JK KR';
|
||||||
|
font-weight: bold;
|
||||||
padding: 0 2em 0 4em;
|
padding: 0 2em 0 4em;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-google {
|
.btn-google {
|
||||||
|
|||||||
145
src/main/resources/static/js/editor.js
Normal file
145
src/main/resources/static/js/editor.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
|
||||||
|
const contents = document.getElementById("content");
|
||||||
|
|
||||||
|
toastui.Editor.setLanguage(['ko', 'ko-KR'], {
|
||||||
|
Markdown: '마크다운',
|
||||||
|
WYSIWYG: '일반',
|
||||||
|
Write: '편집하기',
|
||||||
|
Preview: '미리보기',
|
||||||
|
Headings: '제목크기',
|
||||||
|
Paragraph: '본문',
|
||||||
|
Bold: '굵게',
|
||||||
|
Italic: '기울임꼴',
|
||||||
|
Strike: '취소선',
|
||||||
|
Code: '인라인 코드',
|
||||||
|
Line: '문단나눔',
|
||||||
|
Blockquote: '인용구',
|
||||||
|
'Unordered list': '글머리 기호',
|
||||||
|
'Ordered list': '번호 매기기',
|
||||||
|
Task: '체크박스',
|
||||||
|
Indent: '들여쓰기',
|
||||||
|
Outdent: '내어쓰기',
|
||||||
|
'Insert link': '링크 삽입',
|
||||||
|
'Insert CodeBlock': '코드블럭 삽입',
|
||||||
|
'Insert table': '표 삽입',
|
||||||
|
'Insert image': '이미지 삽입',
|
||||||
|
Heading: '제목',
|
||||||
|
'Image URL': '이미지 주소',
|
||||||
|
'Select image file': '이미지 파일을 선택하세요.',
|
||||||
|
'Choose a file': '파일 선택',
|
||||||
|
'No file': '선택된 파일 없음',
|
||||||
|
Description: '설명',
|
||||||
|
OK: '확인',
|
||||||
|
More: '더 보기',
|
||||||
|
Cancel: '취소',
|
||||||
|
File: '파일',
|
||||||
|
URL: '주소',
|
||||||
|
'Link text': '링크 텍스트',
|
||||||
|
'Add row to up': '위에 행 추가',
|
||||||
|
'Add row to down': '아래에 행 추가',
|
||||||
|
'Add column to left': '왼쪽에 열 추가',
|
||||||
|
'Add column to right': '오른쪽에 열 추가',
|
||||||
|
'Remove row': '행 삭제',
|
||||||
|
'Remove column': '열 삭제',
|
||||||
|
'Align column to left': '열 왼쪽 정렬',
|
||||||
|
'Align column to center': '열 가운데 정렬',
|
||||||
|
'Align column to right': '열 오른쪽 정렬',
|
||||||
|
'Remove table': '표 삭제',
|
||||||
|
'Would you like to paste as table?': '표형태로 붙여 넣겠습니까?',
|
||||||
|
'Text color': '글자 색상',
|
||||||
|
'Auto scroll enabled': '자동 스크롤 켜짐',
|
||||||
|
'Auto scroll disabled': '자동 스크롤 꺼짐',
|
||||||
|
'Choose language': '언어 선택',
|
||||||
|
})
|
||||||
|
|
||||||
|
const editor = new toastui.Editor({
|
||||||
|
el: document.querySelector('#editor'),
|
||||||
|
height: '500px',
|
||||||
|
initialEditType: 'markdown',
|
||||||
|
previewStyle: 'vertical',
|
||||||
|
language: 'ko',
|
||||||
|
toolbarItems: [
|
||||||
|
['heading', 'bold', 'italic', 'strike'],
|
||||||
|
['hr'],
|
||||||
|
['ul', 'ol'],
|
||||||
|
['code'],
|
||||||
|
['table', 'image', 'link']
|
||||||
|
],
|
||||||
|
hooks: {
|
||||||
|
addImageBlobHook: (blob, callback) => {
|
||||||
|
let imgurl = uploadImage(blob);
|
||||||
|
callback(imgurl, "첨부 이미지")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const editorMobile = new toastui.Editor({
|
||||||
|
el: document.querySelector('#editorMobile'),
|
||||||
|
height: 'auto',
|
||||||
|
language: 'ko',
|
||||||
|
initialEditType: 'wysiwyg',
|
||||||
|
previewStyle: 'tab',
|
||||||
|
toolbarItems: [
|
||||||
|
['heading', 'bold', 'italic', 'strike'],
|
||||||
|
['hr'],
|
||||||
|
['ul', 'ol'],
|
||||||
|
['code']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.setMarkdown(contents.value);
|
||||||
|
editorMobile.setMarkdown(contents.value);
|
||||||
|
|
||||||
|
|
||||||
|
function uploadImage(blob) {
|
||||||
|
let token = getCsrfToken();
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('img', blob);
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "/board/uploadImg", false);
|
||||||
|
xhr.setRequestHeader("contentType", "multipart/form-data");
|
||||||
|
xhr.setRequestHeader("X-XSRF-TOKEN", token);
|
||||||
|
xhr.send(formData);
|
||||||
|
|
||||||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||||
|
return xhr.response;
|
||||||
|
} else {
|
||||||
|
alert("이미지가 정상적으로 업로드되지 못했습니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTitle() {
|
||||||
|
|
||||||
|
let title = document.getElementById("title");
|
||||||
|
|
||||||
|
if (title.value === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function post() {
|
||||||
|
|
||||||
|
if (!checkTitle()) {
|
||||||
|
alert("제목을 입력해주세요")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contents.value = editor.getMarkdown();
|
||||||
|
document.getElementById("writeArticleForm").submit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function postMobile() {
|
||||||
|
|
||||||
|
if (!checkTitle()) {
|
||||||
|
alert("제목을 입력해주세요")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.value = editorMobile.getMarkdown();
|
||||||
|
document.getElementById("writeArticleForm").submit();
|
||||||
|
|
||||||
|
}
|
||||||
122
src/main/resources/static/package-lock.json
generated
122
src/main/resources/static/package-lock.json
generated
@@ -1,27 +1,121 @@
|
|||||||
{
|
{
|
||||||
"name": "static",
|
"name": "static",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"dependencies": {
|
||||||
"": {
|
"@toast-ui/editor": {
|
||||||
"version": "1.0.0",
|
"version": "3.1.1",
|
||||||
"license": "ISC",
|
"resolved": "https://registry.npmjs.org/@toast-ui/editor/-/editor-3.1.1.tgz",
|
||||||
"dependencies": {
|
"integrity": "sha512-9Js9+qpg6rMlPZjJLf96sFd87Wm+rc3N+CqQ4RWuqNvpYQLE/YifXHlJkaLIZTpYx40L08gkv205xKq6Ifa9TQ==",
|
||||||
"bootstrap": "^5.1.3"
|
"requires": {
|
||||||
|
"dompurify": "^2.3.3",
|
||||||
|
"prosemirror-commands": "^1.1.9",
|
||||||
|
"prosemirror-history": "^1.1.3",
|
||||||
|
"prosemirror-inputrules": "^1.1.3",
|
||||||
|
"prosemirror-keymap": "^1.1.4",
|
||||||
|
"prosemirror-model": "^1.14.1",
|
||||||
|
"prosemirror-state": "^1.3.4",
|
||||||
|
"prosemirror-view": "^1.18.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bootstrap": {
|
|
||||||
"version": "5.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
|
|
||||||
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q=="
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"bootstrap": {
|
"bootstrap": {
|
||||||
"version": "5.1.3",
|
"version": "5.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
|
||||||
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q=="
|
"integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q=="
|
||||||
|
},
|
||||||
|
"dompurify": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg=="
|
||||||
|
},
|
||||||
|
"orderedmap": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ=="
|
||||||
|
},
|
||||||
|
"prosemirror-commands": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-+CrMs3w/ZVPSkR+REg8KL/clyFLv/1+SgY/OMN+CB22Z24j9TZDje72vL36lOZ/E4NeRXuiCcmENcW/vAcG67A==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-model": "^1.0.0",
|
||||||
|
"prosemirror-state": "^1.0.0",
|
||||||
|
"prosemirror-transform": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-history": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-state": "^1.2.2",
|
||||||
|
"prosemirror-transform": "^1.0.0",
|
||||||
|
"rope-sequence": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-inputrules": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-state": "^1.0.0",
|
||||||
|
"prosemirror-transform": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-keymap": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-state": "^1.0.0",
|
||||||
|
"w3c-keyname": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-model": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-hQJv7SnIhlAy9ga3lhPPgaufhvCbQB9tHwscJ9E1H1pPHmN8w5V/lURueoYv9Kc3/bpNWoyHa8r3g//m7N0ChQ==",
|
||||||
|
"requires": {
|
||||||
|
"orderedmap": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-state": {
|
||||||
|
"version": "1.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
|
||||||
|
"integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-model": "^1.0.0",
|
||||||
|
"prosemirror-transform": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-transform": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-9NLVXy1Sfa2G6qPqhWMkEvwQQMTw7OyTqOZbJaGQWsCeH3hH5Cw+c5eNaLM1Uu75EyKLsEZhJ93XpHJBa6RX8A==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-model": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prosemirror-view": {
|
||||||
|
"version": "1.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.21.0.tgz",
|
||||||
|
"integrity": "sha512-Pjh0vXw/l2Ifc3QcBVKJ/qHej33MX4sdq4/uAVqkSmHLf//bVWTYSZibihMya3lsIxTbDzNl/Z03KrhXWMHaoA==",
|
||||||
|
"requires": {
|
||||||
|
"prosemirror-model": "^1.14.3",
|
||||||
|
"prosemirror-state": "^1.0.0",
|
||||||
|
"prosemirror-transform": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rope-sequence": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg=="
|
||||||
|
},
|
||||||
|
"w3c-keyname": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@toast-ui/editor": "^3.1.1",
|
||||||
"bootstrap": "^5.1.3"
|
"bootstrap": "^5.1.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
src/main/resources/templates/articleWriteForm.html
Normal file
95
src/main/resources/templates/articleWriteForm.html
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html th:replace="~{layout/layout.html :: layout(~{::head}, ~{::section})}"
|
||||||
|
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>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<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://uicdn.toast.com/editor/latest/toastui-editor.min.css"/>
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
<script src="https://kit.fontawesome.com/233840a552.js" crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 150px"></div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center mt-5 mb-3 g-0">
|
||||||
|
|
||||||
|
<form class="" method="post" enctype="multipart/form-data" th:object="${newArticleDto}"
|
||||||
|
th:action="@{/article/write}" id="writeArticleForm">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input id="title" name="title" type="text" class="form-control"
|
||||||
|
placeholder="제목을 입력해주세요" required max="30">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea type="text" name="content" id="content" hidden></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row justify-content-center mb-3 g-0">
|
||||||
|
<label for="editor"></label>
|
||||||
|
<div class=" d-none d-sm-block">
|
||||||
|
<div id="editor"></div>
|
||||||
|
</div>
|
||||||
|
<div class=" d-block d-sm-none">
|
||||||
|
<div id="editorMobile"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-evenly mb-5 g-0">
|
||||||
|
<button class="btn btn-secondary col-4 d-none d-sm-block" onclick="post()">등 록</button>
|
||||||
|
<button class="btn btn-secondary col-4 d-block d-sm-none" onclick="postMobile()">등 록</button>
|
||||||
|
<button class="btn btn-secondary col-4" onclick="javascript:history.back()">취 소</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--scripts-->
|
||||||
|
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
|
||||||
|
<script src="/js/editor.js"></script>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,58 +1,21 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:fragment="layout (title, content, style)"
|
<html th:fragment="layout (head, content)"
|
||||||
lang="ko" xmlns:th="http://www.thymeleaf.org"
|
lang="ko" xmlns:th="http://www.thymeleaf.org"
|
||||||
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
|
||||||
|
|
||||||
<head>
|
<head th:replace="${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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header class="fixed-top d-xxl-none p-0">
|
<header class="fixed-top d-xxl-none p-0">
|
||||||
<nav class="navbar navbar-light bg-light">
|
<nav class="navbar navbar-light bg-light">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid p-0">
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasMenu"
|
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasMenu"
|
||||||
aria-controls="offcanvasMenu">
|
aria-controls="offcanvasMenu">
|
||||||
<i class="fas fa-bars"></i>
|
<i class="fas fa-bars"></i>
|
||||||
</button>
|
</button>
|
||||||
<div id="nav-brand">
|
<div id="nav-brand">
|
||||||
<a href="#">
|
<a th:href="@{/}">
|
||||||
<h4>Blog</h4>
|
<h4>Blog</h4>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,7 +23,8 @@
|
|||||||
class="far fa-address-card"></i></a></div>
|
class="far fa-address-card"></i></a></div>
|
||||||
<div sec:authorize="isAuthenticated()">
|
<div sec:authorize="isAuthenticated()">
|
||||||
<span sec:authorize="isAuthenticated()" th:text="${#authentication.name} + '님'"></span>
|
<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>
|
<a th:href="@{/article/write}"><span sec:authorize="hasRole('ADMIN')" style="font-size: 21px"><i
|
||||||
|
class="fas fa-edit"></i></span></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -106,6 +70,7 @@
|
|||||||
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
|
<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">카테고리2</a></li>
|
||||||
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-1">
|
<li class="mb-1">
|
||||||
@@ -118,6 +83,7 @@
|
|||||||
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
|
<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">카테고리2</a></li>
|
||||||
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-1">
|
<li class="mb-1">
|
||||||
@@ -130,6 +96,7 @@
|
|||||||
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
|
<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">카테고리2</a></li>
|
||||||
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="border-top my-3"></li>
|
<li class="border-top my-3"></li>
|
||||||
@@ -206,6 +173,7 @@
|
|||||||
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
|
<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">카테고리2</a></li>
|
||||||
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-1">
|
<li class="mb-1">
|
||||||
@@ -218,6 +186,7 @@
|
|||||||
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
|
<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">카테고리2</a></li>
|
||||||
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="mb-1">
|
<li class="mb-1">
|
||||||
@@ -230,6 +199,7 @@
|
|||||||
<li><a href="#" class="link-dark rounded">카테고리1</a></li>
|
<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">카테고리2</a></li>
|
||||||
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
<li><a href="#" class="link-dark rounded">카테고리3</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="border-top my-3"></li>
|
<li class="border-top my-3"></li>
|
||||||
@@ -270,6 +240,14 @@
|
|||||||
<div class="col-xxl-10 p-0 sections-container">
|
<div class="col-xxl-10 p-0 sections-container">
|
||||||
|
|
||||||
<section th:replace="${content}"></section>
|
<section th:replace="${content}"></section>
|
||||||
|
|
||||||
|
<button class="arrow-up">
|
||||||
|
<i class="fas fa-arrow-up"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script src="/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- scripts e -->
|
||||||
|
|
||||||
<!-- sections e -->
|
<!-- sections e -->
|
||||||
|
|
||||||
<footer class="footer bg-light">
|
<footer class="footer bg-light">
|
||||||
@@ -277,18 +255,11 @@
|
|||||||
<h5><span class="text-muted">Copyright ©Jinia</span></h5>
|
<h5><span class="text-muted">Copyright ©Jinia</span></h5>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<!-- body e -->
|
<!-- 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>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,22 +1,58 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:replace="~{layout/layout.html :: layout(~{::title}, ~{::section}, ~{::link})}"
|
<html th:replace="~{layout/layout.html :: layout(~{::head}, ~{::section})}"
|
||||||
lang="ko" xmlns:th="http://www.thymeleaf.org"
|
lang="ko" xmlns:th="http://www.thymeleaf.org"
|
||||||
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns="http://www.w3.org/1999/html">
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns="http://www.w3.org/1999/html">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Jinia's Log - 로그인</title>
|
<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 rel="stylesheet" href="/css/login.css"/>
|
<link rel="stylesheet" href="/css/login.css"/>
|
||||||
|
|
||||||
|
<script src="https://kit.fontawesome.com/233840a552.js" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
||||||
<div style="margin-bottom: 300px"></div>
|
<div style="margin-bottom: 150px"></div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<P class="text-center" id="capsAlert" th:text="${errMsg}">로그인 오류</P>
|
||||||
|
<span class="d-flex flex-column align-items-center justify-content-center h-100">
|
||||||
|
|
||||||
<P class="text-center field-error mt-3" id="capsAlert" th:text="${errMsg}">로그인 오류</P>
|
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<a href="/oauth2/authorization/google">
|
<a href="/oauth2/authorization/google">
|
||||||
@@ -42,10 +78,10 @@
|
|||||||
<button class="btn-naver"><img src="/img/naver.png"></button>
|
<button class="btn-naver"><img src="/img/naver.png"></button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-bottom: 300px"></div>
|
<div style="margin-bottom: 100px"></div>
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,12 +1,48 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:replace="~{layout/layout.html :: layout(~{::title}, ~{::section}, ~{::link})}"
|
<html th:replace="~{layout/layout.html :: layout(~{::head}, ~{::section})}"
|
||||||
lang="ko" xmlns:th="http://www.thymeleaf.org"
|
lang="ko" xmlns:th="http://www.thymeleaf.org"
|
||||||
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Jinia's Log</title>
|
<title>Jinia's Log</title>
|
||||||
<link rel="stylesheet" href=""/>
|
<!-- 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 rel="stylesheet" href="/css/login.css"/>
|
||||||
|
|
||||||
|
<script src="https://kit.fontawesome.com/233840a552.js" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package myblog.blog.article.service;
|
||||||
|
|
||||||
|
import myblog.blog.article.dto.NewArticleDto;
|
||||||
|
import myblog.blog.article.repository.ArticleRepository;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.annotation.Rollback;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@Transactional
|
||||||
|
@Rollback(value = false)
|
||||||
|
class ArticleServiceTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ArticleService articleService;
|
||||||
|
@Autowired
|
||||||
|
ArticleRepository articleRepository;
|
||||||
|
@Autowired
|
||||||
|
EntityManager entityManager;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void 게시글테스트() throws Exception {
|
||||||
|
// given
|
||||||
|
NewArticleDto newArticleDto = new NewArticleDto();
|
||||||
|
newArticleDto.setTitle("abs");
|
||||||
|
newArticleDto.setMemberId(1L);
|
||||||
|
newArticleDto.setToc("df");
|
||||||
|
newArticleDto.setContent("sdfsf");
|
||||||
|
|
||||||
|
// when
|
||||||
|
Long articleId = articleService.writeArticle(newArticleDto);
|
||||||
|
|
||||||
|
// then
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println(articleRepository.findById(articleId).get().getContent());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user