728x90
반응형
✔ 게시판 프로젝트
게시판 프로젝트
개발환경
- 개발도구 : 인텔리제이
- 소스코드 관리 : Git
- Git 호스팅 : GitHub
- Git GUI : 깃크라켄 (https://www.gitkraken.com/)
- SDK : JDK 17
목표
- 누구나 사용하기 쉽게 명확한 기능의 요구사항을 만듬
- 요규사항을 구현하는데 도움이 되는 각종 문서 작업을 경험
- 자바 + 스프링부트로 요규사항을 실제로 구현하는 기술적인 방법을 습득
- 최신 버전의 기술을 사용하면서 기술 동향을 파악하고 새로운 기술들을 익혀봄
- 기획과 문서 작성부터 개발, 형상관리, 테스트. 배포까지 개발 프로세스 전반을 경험
문서작업
https://www.diagrams.net/ (draw.io) : 도메인과 ERD 설계
구글 시트 : API 문서
깃 + 깃헙 : 커밋 메시지 작성, 프로젝트 관리 및 협업 환경 생성
다양한 형태의 문서작업
- 업무의 가이드
- 생산성을 높여줌
- 나은 방법을 제안하거나 오류를 잡아줌
- 내용이 구체적일 수록 동료들의 프로젝트 개발 내용이 잘 동기화되고 진행이 막히지 않음
(주의 : 업데이트가 중요, 잘못된 정보가 주는 혼란)
- 백업이 용이, 문서는 지나간 일을 다시 꺼내야 할 때 쉽게 찾기를 도와줌
- 기억은 짧고 왜곡되지만, 문서는 수정 가능하고 발전하며 오래 감
- 업무 기록을 남김으로써 업무 진척 상황과 내 성과가 잘 드러남
개발 목적
- 고객이 원치 않거나 고객의 문제를 해결해줄 수 없는 개발은 의미가 없음
- 가능한 최신 버전 기술과 트렌드 적인 기술을 사용
테스트와 배포
- 테스트
개발 요규사항이 빠짐 없이 모두 구현되었는지 확인
구현도한 요구사항이 오류없이 동작하는 확인
- 베포
클라우드 서버에 배포(헤로쿠)
애자일(agile methodology)
신속한 반복작업을 통해 실제 작동 가능한 소프트웨어를 개발하여 지속적으로 제공하기 위한 소프트웨어 개발 방식
지라(Jira)
애자일 방법론을 기반으로 만들어진 소프트웨어
깃 브랜치 전략
깃 브랜치를 운영하는 방법론을
gitflow
- 브랜치를 설정하고 운영하는 방식
Master : 제품으로 출시될 수 있는 브랜치
Develop : 다음 출시 버전을 개발하는 브랜치
Feature : 기능을 개발하는 브랜치
Release : 이전 출시 버전을 준비하는 브랜치
Hotfix : 출시 버전에서 발샌한 버그를 수정하는 브랜치
github flow
- 브랜치만으로 운영하는 방식
main : 제품으로 출시될 수 있는 브랜치
Feature : 기능을 개발하는 브랜치
✔ application.yaml
debug: false
management.endpoints.web.exposure.include: "*"
logging:
level:
com.koreait.projectboard: debug
org.springframework.web.servlet: debug
org.hibernate.type.descriptor.sql.BasicBinder: trace
spring:
datasource:
url: jdbc:mysql://localhost:3306/aidev
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
defer-datasource-initialization: true
hibernate.ddl-auto: create
show-sql: true
properties:
hibernate.format_sql: true
hibernate.default_batch_fetch_size: 100
# h2.console.enabled: true
sql.init.mode: always
server:
port: 8888
✔ config
package com.koreait.projectboard.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import java.util.Optional;
@EnableJpaAuditing
@Configuration
public class JpaConfig {
@Bean// 데이터를 넣어주기 위해 사용하며 세팅되지 않은
public AuditorAware<String> auditorAware(){
return () -> Optional.of("관리자"); // 스프링 시큐리티로 인증 기능을 붙이게 될 때 수정할거임
}
}
✔ ArticleRepository
package com.koreait.projectboard.repository;
import com.koreait.projectboard.domain.Article;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ArticleRepository extends JpaRepository<Article, Long> {
}
JpaRepossitort : Spring FramWork의 데이터 검색 시 편리하게 검색 할 수 있도록 한다. 검색 메소드(findByID)를 사용할 수 있게 된다. -> JpaRepossitort <엔티티 , ID 유형> 형식으로 작성
✔ Domain - Article
@Getter
@ToString
@Table(indexes = { //@Table은 엔티티와 매핑할 테이블을 지정
@Index(columnList = "title"),
@Index(columnList = "hashtag"),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
// name을 추가하면 테이블이름이 name값으로 설정이 되고 생략시 Entity이름으로 테이블이 만들어지는 것을 확인할 수 있다.
})
@EntityListeners(AuditingEntityListener.class)
@Entity
//@Entity 어노테이션은 JPA를 사용해 테이블과 매핑할 클래스에 붙여주는 어노테이션이다. 이 어노테이션을 붙임으로써 JPA가 해당 클래스를 관리하게 된다.
public class Article {
@Id// Entity 입력할 경우 프라이머리 키를 선언해 줘야한다.
@GeneratedValue(strategy = GenerationType.IDENTITY) // 기본 키 생성을 DB에 위임 (Mysql)
private Long id;
@Setter @Column(nullable = false) private String title; // 제목
@Setter @Column(nullable = false, length = 10000) private String content; // 본문
@Setter private String hashtag; // 해시태그
@CreatedDate @Column(nullable = false) private LocalDateTime createdAt; // 생성일시 수정 시 자동 업데이트
@CreatedBy @Column(nullable = false, length = 100) private String createdBy; // 생성자
@LastModifiedDate @Column(nullable = false) private LocalDateTime modifiedAt; // 수정일시
@LastModifiedBy @Column(nullable = false, length = 100) private String modifiedBy; // 수정자
@ToString.Exclude
@OrderBy("id")
@OneToMany(mappedBy = "article", cascade = CascadeType.ALL)
private final Set<ArticleComment> articleComments = new LinkedHashSet<>();
protected Article() {}
private Article(String title, String content, String hashtag) {
this.title = title;
this.content = content;
this.hashtag = hashtag;
}
public static Article of(String title, String content, String hashtag) {
return new Article(title, content, hashtag);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof Article article)) return false;
return id != null && id.equals(article.id);
}
}
✔ ArticleCommentRepository
package com.koreait.projectboard.repository;
import com.koreait.projectboard.domain.ArticleComment;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ArticleCommentRepository extends JpaRepository<ArticleComment, Long> {
}
✔ Domain - ArticleComment
package com.koreait.projectboard.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Objects;
@Getter
@ToString
@Table(indexes = {
@Index(columnList = "content"),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@EntityListeners(AuditingEntityListener.class)
@Entity
public class ArticleComment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Setter @ManyToOne(optional = false) private Article article; //게시글(id)
@Setter @Column(nullable = false, length = 500) private String content; // 본문
@CreatedDate @Column(nullable = false) private LocalDateTime createdAt; // 생성일시
@CreatedBy @Column(nullable = false) private String createdBy; // 생성자
@LastModifiedDate @Column(nullable = false) private LocalDateTime modifiedAt; //수정일시
@LastModifiedBy @Column(nullable = false) private String modifiedBy; // 수정자
protected ArticleComment() {}
private ArticleComment (Article article, String content)
{
this.article = article;
this.content = content;
}
public static ArticleComment of (Article article, String content) {
return new ArticleComment(article, content); // 외부에서 받아올 수 있도록 선언해주기
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof ArticleComment articleComment)) return false;
return id != null && id.equals(articleComment.id);
}
}
✔ Test - Select
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
@Import(JpaConfig.class)
@DisplayName("JPA 연결 테스트")
@DataJpaTest
class JpaRepositoryTest {
private final ArticleRepository articleRepository;
private final ArticleCommentRepository articleCommentRepository;
public JpaRepositoryTest(
@Autowired ArticleRepository articleRepository,
@Autowired ArticleCommentRepository articleCommentRepository
){
this.articleRepository = articleRepository;
this.articleCommentRepository = articleCommentRepository;
}
@DisplayName("select 테스트")
@Test
void select(){
List<Article> articles = articleRepository.findAll();
assertThat(articles).isNotNull().hasSize(1000);
}
✔ Test - Isert
@DisplayName("insert test")
@Test
void insert(){
long prevCount = articleRepository.count();
Article saveArticle = articleRepository.save(Article.of("새로운 글","새로운 글을 등록합니다","#new"));
//Article.of : 내용 집어넣기
assertThat(articleRepository.count()).isEqualTo(prevCount+1);
// 이전 데이터보다 늘어났으면 통과 한다는 의미이다. 추가한 경우 늘어나야 하기 때문이다. 비교하는 것을 의미(assertThat)
}
@DisplayName("insert test(해당 콘솔 창에 원하는 이름)") : 사용 시 하단 그림과 같이 어떤 콘솔창이 나오는지 알 수 있다.
✔ Test - Update
@DisplayName("update test")
@Test
void update(){
Article article =articleRepository.findById(1l).orElseThrow();
//orElseThrow() ; exception을 던짐 찾는것이 없으면
String updateHashTag = "#Spring";
article.setHashtag(updateHashTag);
Article savedArticle = articleRepository.saveAndFlush(article);// 재 시작 시 드랍되어 보지 못하는 경우 플러시로 확인가능하게
assertThat(savedArticle).hasFieldOrPropertyWithValue("hashtag",updateHashTag ); // 내가 업데이트 한것이 맞는지
}
✔ Test - delete
@DisplayName("delete test")
@Test
void delete(){
Article article = articleRepository.findById(1l).orElseThrow();
long preArticleCount = articleRepository.count(); // 1000개 가 들어왔는지 확인
long preArticleCommentCount = articleCommentRepository.count(); // 댓글 수를 확인
int deletedCommentsSize = article.getArticleComments().size();// 댁글 수
articleRepository.delete(article); // 원본 글을 삭제
assertThat(articleRepository.count()).isEqualTo(preArticleCount -1); // 삭제니 -1인지 확인
assertThat(articleCommentRepository.count()).isEqualTo(preArticleCommentCount -deletedCommentsSize);
//뺀 값과 같은지 확인을 해봐야 하니 빼서 값이 맞는지 확인을 해야 하낟.
}
}
반응형
'Spring' 카테고리의 다른 글
Spring 시험 (7번), Thymleaf 이용 리스트 출력 (1) | 2023.01.09 |
---|---|
Spring Project Board 2 (Repository, DTO, PageController) - 타임리프 SQL 출력 (1) | 2023.01.08 |
Spring 유용한 Setting, 사이트 (0) | 2022.12.29 |
Spring 타임리프 이용 기본 (0) | 2022.12.19 |
Spring Main Page Project (0) | 2022.12.18 |