티스토리 뷰

(구)게시판 프로젝트

회원가입

_Bibidi 2021. 4. 26. 23:56

 

[ 회원가입 기획 ]

 * Reference 1. germweapon.tistory.com/384#:~:text=%EA%B7%B8%EB%9F%B0%EB%8D%B0%20%EC%95%84%EC%9D%B4%EB%94%94%EC%99%80%20%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8%20%EC%83%9D%EC%84%B1,%EC%9D%84%20%EC%A1%B0%ED%95%A9%ED%95%B4%EC%95%BC%20%ED%95%9C%EB%8B%A4%EA%B3%A0%20%ED%95%9C%EB%8B%A4. : 회원가입 기획

 

[ 한글 깨짐 ]

 - 페이지를 jsp include를 이용해서 삽입하거나 하는 경우 삽입되는 페이지 내에

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>가 있어야 한글이 안 깨짐.

 - 스프링으로 들어오는 데이터의 한글이 깨지는 경우 필터 문제. 시큐리티를 이용할 땐 필터를 먼저 적용하고 시큐리티를 적용하도록 해야 깨지지 않는다.

 

 * Reference

1. codevang.tistory.com/266 : 커스터마이징에 대해 상당히 깔금하게 잘 정리되어 있음. 이것만 있으면 밑에 찾은 레퍼런스들이 크게 필요없을 것 같다. 그리고 spring docs 등

 

 

[ Grid System ]

 - 지금 사용하고 있는 부트스트랩 라이브러리 예제에서 class="col-md-4 col-md-offset-4" 이런 식으로 클래스 지정해서 패널 크기나 오프셋 조정하길래 어딘가 정의된 내용이 있는 줄 알았지만 그게 아니였다. 부트스트랩에서 기본으로 지원해주는 Grid System인 것 같다. 아래 reference를 참고해서 공부할 것

 

 * Reference

1. getbootstrap.com/docs/4.0/layout/grid/ : Bootstrap Grid System

 

 

[ MyBatis Tips ]

 - insert, update, delete 쿼리는 각각 영향을 미친 row 개수를 반환한다. insert도 multi insert를 지원하는 경우 2 이상의 값을 반환한다고 하더라. 아직 확인은 안 해봄.

 - #{}의 경우 parameter를 String으로 변환해서 가져간 다음 형변환을 해서 SQL Injection을 막을 수 있다. 보안 측면에서 ${}보다 뛰어나다. ${}의 경우 바로 parameter를 출력하기 때문에 이러한 공격을 막을 수 없다. 사용자의 입력을 받을 땐 ${}를 사용해서는 안 된다고 하는데, 이걸 써야하는 이유가 마땅히 없어서 쓸 일이 있을지 모르겠다.

 - ParameterType 속성 때문인지 기본적으로 하나의 parameter만 보낼 수 있다. 여러 개를 보내려면 VO를 정의하거나 map을 이용하거나 @Param()을 이용하면 된다. parameter의 개수가 적다면 @Param을 이용해서 바로바로 보내는 것이 편하다.

 - resultMap을 한 곳에 정의하고 불러서 사용할 수 있다. resultMap을 중복해서 선언하고 사용하는 것을 지양할 수 있다. 근데 난 안 된다. 이유를 못 찾아서 어쩔 수 없이 resultMap을 필요한 mapper마다 중복 선언함.

 

 * Reference

1. deoki.tistory.com/55 : 다른 mapper에 있는 resultMap 불러오는 법.

 

[ UserMapper interface 작성 ]

public interface UserMapper {
	
	public int insertUser(UserVO user);
	
	public UserVO readUserByUserId(String userId);
	
	public int increaseUserActivityScoreByUserId(@Param("userId") String userId, @Param("points") Long points);
	
	public int decreaseUserActivityScoreByUserId(@Param("userId") String userId, @Param("points") Long points);
	
	public int deleteUserByUserId(String userId);
}

 - MyBatis의 구현을 xml에 할 때 함수를 구별하는 방법이 id를 이용한 방법밖에 없다. 따라서 method overloading이 되지 않아 어떤 변수를 이용해서 함수를 작동시키는지 명시해서 작성했다. By가 붙어있지 않은 경우는 UserVO를 사용하는 경우뿐이다. InsertUserByUser라고 지으면 User를 이용해서 User를 넣는다는 의미가 되는데, 어떻게 봐도 말이 이상하다.

 - insert, update, delete 모두 return type으로 int를 지정했다. insert의 경우 보통 반환하는 것이 없어서 void로 두고 오류가 안 나면 성공이라는 식으로 많이 짜던데, mybatis의 경우 insert를 성공하면 insert한 개수를 반환해준다고 한다. multi insert를 테스트해보지 않아서 1 보다 큰 값도 반환하는지는 모르겠지만 일단 insert는 성공하면 1을 반환한다.

 

[ UserMapper 구현 ]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bibidi.mapper.UserMapper">

	<resultMap type="com.bibidi.domain.UserVO" id="userMap">
		<id property="number" column="user_number"/>
		<result property="number" column="user_number"/>
		<result property="id" column="user_id"/>
		<result property="password" column="user_password"/>
		<result property="userEmail" column="user_email"/>
		<result property="nickname" column="user_nickname"/>
		<result property="picture" column="user_picture"/>
		<result property="activityScore" column="user_activity_score"/>
		<result property="dateRegistered" column="date_user_created"/>
		<result property="dateModified" column="date_user_modified"/>
		<result property="enabled" column="user_enabled"/>
		<collection property="roleList" resultMap="roleMap"/>
	</resultMap>
	
	<resultMap type="com.bibidi.domain.RoleVO" id="roleMap">
		<id property="number" column="role_number"/>
		<result property="number" column="role_number"/>
		<result property="name" column="role_name"/>
	</resultMap>
	
	<insert id="insertUser">
		INSERT INTO users
			(user_number, user_id, user_password, user_email, user_nickname, user_activity_score)
		VALUES
			(seq_user.nextval, #{id}, #{password}, #{userEmail}, #{nickname}, #{activityScore})
		
	</insert>
	
	<select id="readUserByUserId" resultMap="userMap">
		SELECT
			users.*, roles.*
		FROM users
			LEFT OUTER JOIN users_roles ON users.user_number = users_roles.user_number
			LEFT OUTER JOIN roles ON users_roles.role_number = roles.role_number
		WHERE user_id = #{userId}
	</select>
	
	<update id="increaseUserActivityScoreByUserId">
		UPDATE users
		SET user_activity_score = user_activity_score + #{points}
		WHERE user_id = #{userId}
	</update>

	<update id="decreaseUserActivityScoreByUserId">
		UPDATE users
		SET user_activity_score = user_activity_score - #{points}
		WHERE user_id = #{userId}
	</update>
	
	<delete id="deleteUserByUserId">
		DELETE FROM users
		WHERE user_id = #{userId}
	</delete>
</mapper>

 - update 구문이 제대로 작동하지 않아서 정말 고생했다. 문제는 MyBatis가 Parameter를 기본적으로 하나만 보낼 수 있어서 그랬던 것. @Param을 이용해 해결했다.

 

[ UserMapper Tests ]

@RunWith(SpringRunner.class)
@ContextConfiguration({
	"file:src/main/webapp/WEB-INF/spring/root-context.xml",
	"file:src/main/webapp/WEB-INF/spring/security-context.xml"})
@Log4j
public class UserMapperTests {

	@Setter(onMethod_ = @Autowired)
	private UserMapper userMapper;
	
	@Setter(onMethod_ = @Autowired)
	private PasswordEncoder passwordEncoder;
	
	@Test
	public void testInsertUser() {
		UserVO user = new UserVO();
		user.setId("tid");
		user.setPassword(passwordEncoder.encode("tpd"));
		user.setUserEmail("temail@t.com");
		user.setNickname("tnickname");
		user.setActivityScore(3000L);
		
		log.info("NUMBER OF INSERTED USERS : " 
				+ userMapper.insertUser(user));
	}
	
	@Test
	public void testReadUserByUserId() {
		UserVO user = userMapper.readUserByUserId("bibidi");
		
		log.info(user);
		
		user.getRoleList().forEach(role -> log.info(role));
	}
	
	@Test
	public void testIncreaseUserActivityScoreByUserId() {
		log.info("NUMBER OF USERS WITH INCREASED USERS : " 
				+ userMapper.increaseUserActivityScoreByUserId("bibidi", 100L));
	}
	
	@Test
	public void testDecreaseUserActivityScoreByUserId() {
		log.info("NUMBER OF USERS WITH INCREASED USERS : " 
				+ userMapper.decreaseUserActivityScoreByUserId("bibidi", 100L));
	}
	
	@Test
	public void testDeleteUserByUserId() {
		log.info("NUMBER OF DELETED USER : " 
				+ userMapper.deleteUserByUserId("tid"));
	}
}

 - 기본 동작만 테스트했다. 테스트 주도 개발이나 유닛 테스트 책이 있는 거 보면 테스트할 때 생각해야할 것들이 많은 것 같다. 그런 자료들을 익히고 더 꼼곰한 테스트를 작성할 수 있도록 해야 한다.

 

[ UserService 작성 ]

public interface UserService {

	public int registerUser(UserVO user);
	
	public UserVO getUserByUserId(String userId);
	
	public int increaseUserActivityScoreByUserId(String userId, Long points);
	
	public int decreaseUserActivityScoreByUserId(String userId, Long points);
	
	public int deleteUserByUserId(String userId);
}
@Service
@Log4j
public class UserServiceImpl implements UserService {
	
	@Setter(onMethod_ = @Autowired)
	private UserMapper userMapper;

	@Override
	public int registerUser(UserVO user) {
		
		log.info("registerUser.........");
		
		int countUserRegistered = userMapper.insertUser(user);
		
		return countUserRegistered;
	}

	@Override
	public UserVO getUserByUserId(String userId) {

		log.info("getUserByUserId...........");
		return userMapper.readUserByUserId(userId);
	}

	@Override
	public int increaseUserActivityScoreByUserId(String userId, Long points) {
		
		log.info("increaseUserActivityScoreByUserId...............");
		return userMapper.increaseUserActivityScoreByUserId(userId, points);
	}

	@Override
	public int decreaseUserActivityScoreByUserId(String userId, Long points) {
		
		log.info("decreaseUserActivityScoreByUserId.................");
		return userMapper.decreaseUserActivityScoreByUserId(userId, points);
	}

	@Override
	public int deleteUserByUserId(String userId) {
		
		log.info("deleteUserByUserId................");
		return userMapper.deleteUserByUserId(userId);
	}
	
}

 - Service의 경우 service 구현체에 @Service를 달아줘야 빈으로 제대로 등록된다. mapper와는 방식이 조금 다르다.

 

[ UserServceTests 작성 ]

@RunWith(SpringRunner.class)
@ContextConfiguration({
	"file:src/main/webapp/WEB-INF/spring/root-context.xml",
	"file:src/main/webapp/WEB-INF/spring/security-context.xml"})
@Log4j
public class UserServiceTests {

	@Setter(onMethod_ = @Autowired)
	private UserService userService;
	
	@Setter(onMethod_ = @Autowired)
	private PasswordEncoder passwordEncoder;
	
	@Test
	public void testExist() {
		
		log.info(userService);
		log.info(passwordEncoder);
		assertNotNull(userService);
		assertNotNull(passwordEncoder);
	}
	
	@Test
	public void testRegisterUser() {
		UserVO user= new UserVO();
		user.setId("tid");
		user.setPassword(passwordEncoder.encode("tpwd"));
		user.setUserEmail("temail@t.com");
		user.setNickname("tnickname");
		user.setActivityScore(3000L);
		
		log.info("NUMBER OF REGISTERED USERS : "
				+ userService.registerUser(user));
	}
	
	@Test
	public void testGetUserByUserId() {
		UserVO user = userService.getUserByUserId("bibidi");
		
		log.info(user);
		user.getRoles().forEach(role -> log.info(role));
	}
	
	@Test
	public void testIncreaseUserActivityScoreByUserId() {
		log.info("NUMBER OF USERS WITH INCREASED ACTIVITY SCORE : "
				+ userService.increaseUserActivityScoreByUserId("bibidi", 100L));
	}
	
	@Test
	public void testDecreaseUserActivityScoreByUserId() {
		log.info("NUMBER OF USERS WITH INCREASED ACTIVITY SCORE : "
				+ userService.decreaseUserActivityScoreByUserId("bibidi", 100L));
	}
	
	@Test
	public void testDeleteUserByUserId() {
		log.info("NUMBER OF DELETED USERS : "
				+ userService.deleteUserByUserId("tid"));
	}
}

 - Service test의 경우 책에서 service가 잘 주입되었는지부터 테스트한다. 당장 나부터가 Controller에 바로 service 주입하고 요구사항 처리하려는데 unsatisfiedDependencyException이 터지더라.

 

 

[ 회원가입 화면 ]

회원가입 페이지

 - 아직 폼 유효성 검증 부분은 추가하지 않았다.

 

 

 

 

 

 

 

 

 

 

 

'(구)게시판 프로젝트' 카테고리의 다른 글

이름 짓는 법 총 정리  (1) 2021.04.28
메인 화면  (0) 2021.04.27
로그인  (0) 2021.04.16
프로젝트 생성 및 환경 설정  (0) 2021.04.15
데이터 모델링 2 (updated 21.05.07)  (0) 2021.04.10
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함